Como esperar que todos os processos gerados e em segundo plano terminem no script bash

3

Eu olhei e olhei e não consigo encontrar uma solução de trabalho para um script bash que estou tentando criar para fechar um processo e esperar por ele e gerar processos para terminar. Eu ainda estou aprendendo muito Linux.

Contexto : O processo FOO é executado. O Process BAR é usado para checar o FOO e também é usado para eliminá-lo (não tenho controle sobre os internos desses dois processos). Tudo o que posso fazer é passar comandos para o BAR e executá-los.

Neste caso, envio o comando para o BAR para matar o FOO e ele gera outro processo e origens.

Objetivo : Estou tentando executar 20 comandos simultaneamente para executar kill em 20 FOO (via BAR) e esperar que todos os FOOs morram antes de passar para a próxima parte do script.

Problema : Até agora, tudo que posso fazer é esperar que o BAR seja executado e ele prossiga antes que o processo em segundo plano mate o FOO.

BAR exit FOO1
BAR exit FOO2
...
BAR exit FOO20
wait
do more stuff

Eu também tentei

BAR exit FOO1
PID1=$!
BAR exit FOO2
PID2=$!
wait $PID1 $PID2

sem sorte.

    
por M. Massey 16.01.2016 / 00:19

3 respostas

0

Primeiro, tente um cenário mais simples, como apenas 2 processos FOO1 e FOO2 , e se você fosse executado em um script, por exemplo, denominado parent.sh :

#!/bin/bash

FOO1 &
pid1=$!

echo "Child PID=$pid1 launched"

FOO2 &
pid2=$!

echo "Child PID=$pid2 launched"

BAR exit FOO1
BAR exit FOO2

echo "Pausing to wait for children to finish..."

wait $pid1
wait $pid2

echo "Children finished, moving on."

Veja se isso funciona com os dois FOO s e, se for o caso, implemente para os 20.

Explicação

Como não conseguimos ver as partes internas do processo FOO se BAR , estou confiando apenas no que você postou e, pelo que entendi, você disse

  • "Process BAR é usado para verificar o FOO e também é usado para eliminá-lo"
  • significando que, em algum momento anterior ao seu snippet publicado, você iniciou FOO1 de alguma forma, para que BAR exit FOO1 o afetasse

Entre seu snippet publicado originalmente, você também escreveu:

BAR exit FOO1
PID1=$!
  • $! captura o processo de segundo plano mais recente
  • mas BAR exit FOO1 não é um processo que entra em segundo plano, é como você afirma, um meio de usar BAR para controlar FOO: "BAR é usado para verificar FOO e também é usado para eliminá-lo"
  • assim, é improvável que $! capture% pid
  • de FOO1

Portanto, assegure-se de que, exatamente onde você inicia um processo FOO *, capture pid. Por exemplo, se você estiver executando tudo em parent.sh e tiver apenas dois processos, você faria:

#!/bin/sh

FOO1 &
pid1=$!

echo "Child PID=$pid1 launched"

FOO2 &
pid2=$!

echo "Child PID=$pid2 launched"
  • minúsculas pid1 , você também pode usar maiúsculas originais, desde que consistente, eu prefiro usar minúsculas como convenção para diferenciar de ser confundido com variáveis de ambiente
  • O echo é um rastreio opcional para que saibamos o que está acontecendo, use-o enquanto estivermos solucionando problemas e comente com # ou exclua-o assim que tiver resolvido esse problema

Em seguida, executamos os comandos que você reivindicou para verificar e eliminar FOO1 e FOO2 :

BAR exit FOO1
BAR exit FOO2

Então,

echo "Pausing to wait for children to finish..."

wait $pid1
wait $pid2

echo "Children finished, moving on."
  • novamente, os echo s são opcionais, mas informativos enquanto você ainda está solucionando isso, nos informa o que está acontecendo
  • wait para cada indivíduo $pid ...
  • seguido por outro echo opcional para informar que wait ing acabou

CRÉDITO

Exemplo de scripts assíncronos de

Shotts 2009 na página gratuita do pdf 506

    
por 16.01.2016 / 01:05
0

Você pode tocar em um arquivo para cada comando e verificar esse arquivo. Aqui está um exemplo simples:

#!/usr/local/bin/bash

# Flag to knkow when completed
finished=0;

# The commands t run
declare -a commands=('cmd1' 'cmd2' 'cmd3' 'cmd4');

# Fork all commands
for cmd in "${commands[@]}"; do
        ./$cmd &
done

# Loop to wait for all processes to finish
loop=0;
while [ $finished -eq 0 ]; do
        # Just for visual effect
        loop=$((loop+1));
        echo "Loop $loop";
        sleep 1;

        files=0;
        for cmd in "${commands[@]}"; do
                # Each program shoudl touch a file so that we
                # can know it has completed successfully
                if [ -e ".$cmd" ]; then
                        echo "Process $cmd completed";
                        files=$((files+1));
                else
                        # No need to continue if anything is not done
                        # Error checking here like a TTL value or ps -ef | grep pid etc
                        echo "Waiting for process $cmd";
                        break;
                fi;
        done

        # if we have as many files as executed commands
        if [ "$files" -eq ${#commands[@]} ]; then
                finished=1;
        fi
done;

echo 'All jobs completed successfully';
# Clean up after yourself
for cmd in "${commands[@]}"; do
        rm -f ".$cmd";
done;

Em seguida, para testar cada cmd1, o cmd2 ... cmdN pode ter algo como o seguinte:

#!/usr/local/bin/bash
num=1;

echo "Enter cmd$num";
sleep 1; # use different sleep values to simulate execution
touch ".cmd$num"
echo "Exit cmd$num";
    
por 16.01.2016 / 01:18
-1

Isso será avaliado como verdadeiro se o $ PID1 estiver em execução: if [kill -s 0 $ PID1]

Você pode ter várias cláusulas if. Eles serão avaliados como false se o PID não estiver sendo executado. Esta cláusula (combinada com outras) deve controlar o fluxo do script como você deseja.

    
por 16.01.2016 / 00:28