Multitarefa em scripts de shell

Às vezes, ao escrever um script de shell, você deseja realizar algumas ações em vários threads. Situações adequadas podem ser, por exemplo, compactar um grande número de arquivos grandes em um host multiprocessador e transferir arquivos em uma largura de banda ampla, onde a velocidade de uma conexão individual é limitada.



Todos os exemplos são escritos em bash, mas (com mudanças mínimas) funcionarão em ksh. O Csh também possui um recurso para gerenciar processos em segundo plano, portanto, uma abordagem semelhante também pode ser usada.



CONTROLE DE TRABALHO



Este é o nome da seção do man bash onde os detalhes são descritos, caso você goste de ler man. Usamos os seguintes recursos simples:



command & - executa um comando nos

trabalhos em segundo plano - imprime uma lista de comandos em segundo plano



Um exemplo simples que não executa nenhuma ação útil. Os números são lidos a partir do arquivo test.txt e 3 processos são iniciados em paralelo, que ficam suspensos pelo número de segundos correspondente. A cada três segundos, o número de processos em execução é verificado e, se houver menos de três, um novo é iniciado. O lançamento do processo em segundo plano foi movido para uma função separada mytask, mas você pode iniciá-lo diretamente em um loop.



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




Dados de entrada:



test.txt
60

50

30

21

12

13



Preste atenção à espera após o loop, o comando espera que os processos em execução em segundo plano terminem. Sem ele, o script será encerrado imediatamente após o término do ciclo e todos os processos em segundo plano serão interrompidos. Talvez essa espera seja mencionada no famoso meme "oh, espere !!!".



Encerrando processos em segundo plano



Se você interromper o script com Ctrl-C, ele será encerrado com todos os processos em segundo plano. todos os processos em execução no terminal recebem sinais do teclado (por exemplo, SIGINT). Se o script for eliminado de outro terminal com o comando kill, os processos em segundo plano permanecerão em execução até o término e você precisa se lembrar disso.



Cabeçalho 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



Essa situação pode ser tratada interceptando os sinais necessários, para os quais adicionamos um manipulador no início do script:



armadilha
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 exibe uma lista de sinais existentes, se necessário, você pode adicionar manipuladores para os necessários.



All Articles