انتظر وظائف خلفية باش في البرنامج النصي
-
16-09-2019 - |
سؤال
لتعظيم استخدام وحدة المعالجة المركزية (أركض الأشياء في Debian Lenny في EC2) لدي برنامج نصي بسيط لإطلاق وظائف بالتوازي:
#!/bin/bash
for i in apache-200901*.log; do echo "Processing $i ..."; do_something_important; done &
for i in apache-200902*.log; do echo "Processing $i ..."; do_something_important; done &
for i in apache-200903*.log; do echo "Processing $i ..."; do_something_important; done &
for i in apache-200904*.log; do echo "Processing $i ..."; do_something_important; done &
...
أنا راض تماما عن حل العمل هذا، ومع ذلك، لم أستطع معرفة كيفية كتابة مزيد من التعليمات البرمجية التي تم تنفيذها إلا بمجرد الانتهاء من جميع الحلقات.
هل هناك طريقة للسيطرة على هذا؟
المحلول
هناك bash
الأمر المدمج لذلك.
wait [n ...]
Wait for each specified process and return its termination sta‐
tus. Each n may be a process ID or a job specification; if a
job spec is given, all processes in that job’s pipeline are
waited for. If n is not given, all currently active child pro‐
cesses are waited for, and the return status is zero. If n
specifies a non-existent process or job, the return status is
127. Otherwise, the return status is the exit status of the
last process or job waited for.
نصائح أخرى
سيجعل استخدام GNU ParAllel النصي الخاص بك أقصر وربما أكثر كفاءة:
parallel 'echo "Processing "{}" ..."; do_something_important {}' ::: apache-*.log
سيعمل ذلك على تشغيل وظيفة واحدة لكل وحدة المعالجة المركزية الأساسية وتستمر في القيام بذلك حتى تتم معالجة جميع الملفات.
سوف ينقسم الحل الخاص بك في الأساس الوظائف في مجموعات قبل التشغيل. هنا 32 وظيفة في 4 مجموعات:
تضع GNU متوازي بدلا من ذلك عملية جديدة عند انتهاء المرء - الحفاظ على وحدة المعالجة المركزية النشطة وبالتالي توفير الوقت:
لتعلم المزيد:
- شاهد الفيديو المقدمة مقدمة سريعة:https://www.youtube.com/playlist؟list=PL284C9FF2488BC6D1.
- المشي من خلال البرنامج التعليمي (man parallel_tutorial). سطر الأوامر سوف أحبك لذلك.
اضطررت للقيام بذلك مؤخرا وانتهى الأمر بالحل التالي:
while true; do
wait -n || {
code="$?"
([[ $code = "127" ]] && exit 0 || exit "$code")
break
}
done;
وإليك كيف يعمل:
wait -n
يخرج بمجرد خروج أحد وظائف الخلفية (المحتملة). يقيم دائما إلى TRUE ويتماشى الحلقة حتى:
- رمز الخروج
127
: خرجت وظيفة الخلفية الأخيرة بنجاح. في هذه الحالة، نتجاهل رمز الخروج والخروج من Sub-Shell برمز 0. - فشل أي من مهمة الخلفية. نحن فقط الخروج من شل الفرعية مع هذا الرمز الخروج.
مع set -e
, ، سيضمن هذا أن البرنامج النصي سينتهي مبكرا وتمرير رمز الخروج لأي وظيفة خلفية فاشلة.
هذا هو الحل الخام الخاص بي:
function run_task {
cmd=$1
output=$2
concurency=$3
if [ -f ${output}.done ]; then
# experiment already run
echo "Command already run: $cmd. Found output $output"
return
fi
count=`jobs -p | wc -l`
echo "New active task #$count: $cmd > $output"
$cmd > $output && touch $output.done &
stop=$(($count >= $concurency))
while [ $stop -eq 1 ]; do
echo "Waiting for $count worker threads..."
sleep 1
count=`jobs -p | wc -l`
stop=$(($count > $concurency))
done
}
الفكرة هي استخدام "وظائف" لمعرفة عدد الأطفال النشطين في الخلفية والانتظار حتى يسقط الرقم هذا (مخارج طفل). بمجرد وجود طفل، يمكن بدء المهمة التالية.
كما ترون، يوجد أيضا منطق إضافي لتجنب تشغيل نفس التجارب / الأوامر عدة مرات. إنه يفعل المهمة بالنسبة لي .. ومع ذلك، يمكن تخطي هذا المنطق أو تحسينه (على سبيل المثال، تحقق من طابع الطابع الزمني لإنشاء الملفات، معلمات الإدخال، إلخ).