Multitasking in Shell-Skripten

Manchmal möchten Sie beim Schreiben eines Shell-Skripts einige Aktionen in mehreren Threads ausführen. Geeignete Situationen können beispielsweise das Komprimieren einer großen Anzahl großer Dateien auf einem Multiprozessor-Host und das Übertragen von Dateien über eine große Bandbreite sein, wenn die Geschwindigkeit einer einzelnen Verbindung begrenzt ist.



Alle Beispiele sind in bash geschrieben, funktionieren aber (mit minimalen Änderungen) in ksh. Csh verfügt auch über eine Hintergrundfunktion für das Prozessmanagement, sodass ein ähnlicher Ansatz verwendet werden kann.



JOBKONTROLLE



Dies ist der Name des Abschnitts in man bash, in dem die Details beschrieben werden, falls Sie gerne man lesen. Wir verwenden die folgenden einfachen Funktionen:



Befehl & - führt einen Befehl in den Hintergrund

Jobs - druckt eine Liste von Hintergrund - Befehle



Ein einfaches Beispiel , das keine nützliche Wirkung tut. Zahlen werden aus der Datei test.txt gelesen und 3 Prozesse werden parallel gestartet, die für die entsprechende Anzahl von Sekunden in den Ruhezustand versetzt werden. Alle drei Sekunden wird die Anzahl der ausgeführten Prozesse überprüft, und wenn weniger als drei vorhanden sind, wird ein neuer gestartet. Der Start des Hintergrundprozesses wurde in eine separate Funktion mytask verschoben, Sie können ihn jedoch direkt in einer Schleife starten.



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




Eingabedaten:



test.txt
60

50

30

21

12

13



Achten Sie auf das Warten nach der Schleife. Der Befehl wartet, bis die im Hintergrund ausgeführten Prozesse abgeschlossen sind. Ohne diese Option wird das Skript sofort nach Ende der Schleife beendet und alle Hintergrundprozesse werden unterbrochen. Vielleicht wird dieses Warten im berühmten Mem "Oh, warte !!!" erwähnt.



Hintergrundprozesse beenden



Wenn Sie das Skript mit Strg-C unterbrechen, wird es bei allen Hintergrundprozessen beendet. Alle im Terminal ausgeführten Prozesse empfangen Signale von der Tastatur (z. B. SIGINT). Wenn das Skript mit dem Befehl kill von einem anderen Terminal beendet wird, werden die Hintergrundprozesse bis zur Beendigung ausgeführt, und Sie müssen sich daran erinnern.



Spoiler Header
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



Diese Situation kann durch Abfangen der erforderlichen Signale behoben werden, für die wir am Anfang des Skripts einen Handler hinzufügen:



Falle
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 zeigt eine Liste der vorhandenen Signale an. Bei Bedarf können Sie Handler für die erforderlichen hinzufügen.



All Articles