تعدد المهام في نصوص شل

في بعض الأحيان ، عند كتابة نص برمجي ، تريد تنفيذ بعض الإجراءات في عدة سلاسل. قد تكون المواقف المناسبة ، على سبيل المثال ، ضغط عدد كبير من الملفات الكبيرة على مضيف متعدد المعالجات ، ونقل الملفات عبر نطاق ترددي واسع ، حيث تكون سرعة الاتصال الفردي محدودة.



جميع الأمثلة مكتوبة بلغة bash ، لكن (مع الحد الأدنى من التغييرات) ستعمل في ksh. لدى Csh أيضًا وسيلة لإدارة العمليات الخلفية ، لذلك يمكن أيضًا استخدام نهج مماثل.



التحكم في الوظيفة



هذا هو اسم القسم في man bash حيث تذهب التفاصيل ، في حال كنت تحب قراءة الرجل. نستخدم الميزات البسيطة التالية:



الأمر & - يقوم بتشغيل أمر في

وظائف الخلفية - يطبع قائمة بأوامر الخلفية



مثال بسيط لا يقوم بأي إجراء مفيد. تتم قراءة الأرقام من ملف test.txt ، ويتم تشغيل 3 عمليات بالتوازي ، والتي تنام لعدد الثواني المقابل. كل ثلاث ثوان ، يتم فحص عدد العمليات الجارية ، وإذا كان هناك أقل من ثلاث ، يتم بدء عملية جديدة. تم نقل عملية تشغيل الخلفية إلى وظيفة منفصلة mytask ، ولكن يمكنك البدء بها مباشرة في حلقة.



test.sh
#!/bin/bash 

NJOBS=3 ; export NJOBS

function mytask () {
echo sleeping for $1
 sleep $1
}

for i in $( cat test.txt )
do
    while [  $(jobs | wc -l ) -ge $NJOBS ]
        do 
            sleep 3
        done
    echo executing task for $i
    mytask $i &
done

echo waiting for $( jobs | wc -l ) jobs to complete
wait




ادخال البيانات:



test.txt
60

50

30

21

12

13



انتبه إلى الانتظار بعد الحلقة ، ينتظر الأمر حتى تنتهي العمليات التي تعمل في الخلفية. بدونها ، سيتم إنهاء البرنامج النصي فور انتهاء الحلقة وستتوقف جميع عمليات الخلفية. ربما تم ذكر هذا الانتظار في الميم الشهير "أوه ، انتظر !!!".



إنهاء عمليات الخلفية



إذا قاطعت النص باستخدام Ctrl-C ، فسيتم إعدامه مع جميع عمليات الخلفية. تستقبل جميع العمليات التي تعمل في الجهاز إشارات من لوحة المفاتيح (على سبيل المثال ، SIGINT). إذا تم قتل البرنامج النصي من محطة أخرى باستخدام الأمر kill ، فستظل عمليات الخلفية قيد التشغيل حتى الإنهاء ، وتحتاج إلى تذكر ذلك.



رأس سبويلر
user@somehost ~/tmp2 $ ps -ef | grep -E «test|sleep»

user 1363 775 0 12:31 pts/5 00:00:00 ./test.sh

user 1368 1363 0 12:31 pts/5 00:00:00 ./test.sh

user 1370 1368 0 12:31 pts/5 00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 60

user 1373 1363 0 12:31 pts/5 00:00:00 ./test.sh

user 1375 1373 0 12:31 pts/5 00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 50

user 1378 1363 0 12:31 pts/5 00:00:00 ./test.sh

user 1382 1378 0 12:31 pts/5 00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 30

user 1387 1363 0 12:31 pts/5 00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 3

user 1389 556 0 12:31 pts/2 00:00:00 grep --colour=auto -E test|sleep

user@somehost ~/tmp2 $ kill 1363

user@somehost ~/tmp2 $ ps -ef | grep -E «test|sleep»

user 1368 1 0 12:31 pts/5 00:00:00 ./test.sh

user 1370 1368 0 12:31 pts/5 00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 60

user 1373 1 0 12:31 pts/5 00:00:00 ./test.sh

user 1375 1373 0 12:31 pts/5 00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 50

user 1378 1 0 12:31 pts/5 00:00:00 ./test.sh

user 1382 1378 0 12:31 pts/5 00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 30

user 1399 556 0 12:32 pts/2 00:00:00 grep --colour=auto -E test|sleep



يمكن التعامل مع هذا الموقف عن طريق اعتراض الإشارات الضرورية ، والتي نضيف لها معالجًا في بداية البرنامج النصي:



فخ
function pids_recursive() {
    cpids=`pgrep -P $1|xargs`
    echo $cpids
    for cpid in $cpids;
       do
          pids_recursive $cpid
       done
}

function kill_me () {
    kill -9 $( pids_recursive $$ | xargs )
    exit 1
}

# 
#trap 'echo trap SIGINT; kill_me ' SIGINT
trap 'echo trap SIGTERM; kill_me' SIGTERM




يعرض kill -L قائمة بالإشارات الموجودة ، إذا لزم الأمر ، يمكنك إضافة معالجات للإشارات المطلوبة.



All Articles