Multitarea en scripts de shell

A veces, al escribir un script de shell, desea realizar algunas acciones en varios hilos. Las situaciones adecuadas podrían ser, por ejemplo, comprimir una gran cantidad de archivos grandes en un host multiprocesador y transferir archivos en un ancho de banda amplio, donde la velocidad de una conexión individual es limitada.



Todos los ejemplos están escritos en bash, pero (con cambios mínimos) funcionarán en ksh. Csh también tiene una función para administrar procesos en segundo plano, por lo que también se puede utilizar un enfoque similar.



CONTROL DE TRABAJO



Este es el nombre de la sección en man bash donde van los detalles, en caso de que te guste leer man. Usamos las siguientes funciones simples:



comando & : ejecuta un comando en los

trabajos en segundo plano : imprime una lista de comandos en segundo plano.



Un ejemplo simple que no realiza ninguna acción útil. Los números se leen del archivo test.txt y se inician 3 procesos en paralelo, que duermen durante el número correspondiente de segundos. Cada tres segundos, se comprueba el número de procesos en ejecución y, si hay menos de tres, se inicia uno nuevo. El lanzamiento del proceso en segundo plano se ha movido a una función mytask separada, pero puede iniciarlo directamente en un bucle.



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




Datos de entrada:



test.txt
60

50

30

21

12

13



Preste atención a la espera después del ciclo, el comando espera a que finalicen los procesos que se ejecutan en segundo plano. Sin él, el script terminará inmediatamente después de que finalice el ciclo y se interrumpirán todos los procesos en segundo plano. Quizás esta espera se menciona en el famoso meme "¡¡¡oh, espera !!!".



Terminar procesos en segundo plano



Si interrumpe el script con Ctrl-C, se eliminará con todos los procesos en segundo plano. todos los procesos que se ejecutan en el terminal reciben señales del teclado (por ejemplo, SIGINT). Si el script se mata desde otra terminal con el comando kill, los procesos en segundo plano seguirán ejecutándose hasta la finalización, y debe recordar esto.



Encabezado 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



Esta situación se puede manejar interceptando las señales necesarias, para lo cual agregamos un controlador al comienzo del script:



trampa
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 muestra una lista de señales existentes, si es necesario, puede agregar controladores para las requeridas.



All Articles