Multitâche dans les scripts shell

Parfois, lors de l'écriture d'un script shell, vous souhaitez effectuer certaines actions dans plusieurs threads. Les situations appropriées peuvent être, par exemple, la compression d'un grand nombre de fichiers volumineux sur un hôte multiprocesseur et le transfert de fichiers sur une large bande passante, où la vitesse d'une connexion individuelle est limitée.



Tous les exemples sont écrits en bash, mais (avec des changements minimes) fonctionneront dans ksh. Csh dispose également d'une fonction de gestion des processus d'arrière-plan, de sorte qu'une approche similaire peut également être utilisée.



CONTRÔLE DES EMPLOIS



C'est le nom de la section dans man bash où les détails sont décrits, au cas où vous aimeriez lire man. Nous utilisons les fonctionnalités simples suivantes:



commande & - exécute une commande dans les

travaux d' arrière-plan - imprime une liste de commandes d'arrière-plan



Un exemple simple qui ne fait aucune action utile. Les nombres sont lus à partir du fichier test.txt, et 3 processus sont lancés en parallèle, qui dorment pendant le nombre de secondes correspondant. Toutes les trois secondes, le nombre de processus en cours est vérifié et s'il y en a moins de trois, un nouveau est démarré. Le lancement du processus d'arrière-plan a été déplacé vers une fonction distincte mytask, mais vous pouvez le démarrer directement dans une boucle.



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




Des données d'entrée:



test.txt
60

50

30

21

12

13



Faites attention à l'attente après la boucle, la commande attend que les processus s'exécutant en arrière-plan se terminent. Sans cela, le script se terminera immédiatement après la fin de la boucle et tous les processus d'arrière-plan seront interrompus. Peut-être que cette attente est mentionnée dans le fameux meme "oh, attendez !!!".



Terminer les processus d'arrière-plan



Si vous interrompez le script avec Ctrl-C, il sera tué avec tous les processus d'arrière-plan. tous les processus exécutés dans le terminal reçoivent des signaux du clavier (par exemple, SIGINT). Si le script est tué depuis un autre terminal avec la commande kill, les processus d'arrière-plan resteront en cours d'exécution jusqu'à la fin, et vous devez vous en souvenir.



En-tête de spoiler
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



Cette situation peut être gérée en interceptant les signaux nécessaires, pour lesquels nous ajoutons un gestionnaire au début du script:



piège
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 affiche une liste des signaux existants, si nécessaire, vous pouvez ajouter des gestionnaires pour ceux nécessaires.



All Articles