Bash, como deixar alguns processos em segundo plano serem executados, mas esperar outros?

11

Tenho (ainda) outro wait, &, &&pergunta fluxo de controle ..

Digamos que eu tenha um script parecido com este, onde eu queira fazer o máximo de trabalho possível ao mesmo tempo:

# may take some hours
something InputA > IrrelevantA &
something InputB > IrrelevantB &

# may take an hour
(
   somethingElse InputA > OutputA &
   somethingElse InputB > OutputB &
)&& combine OutputA OutputB > Result

...morestuff

Pergunta 1: No script, combineaguarda somethingElsea conclusão de ambos os somethingprocessos enquanto ambos continuam?

Pergunta 2: Se não - e eu suspeito que não - como posso combineesperar apenas pelos dois somethingElseprocessos, enquanto os somethingprocessos acima continuam funcionando em segundo plano?

Stephen Henderson
fonte

Respostas:

13

No seu exemplo, o combinecomando será executado assim que o subshell sair (e desde que o último processo em segundo plano tenha sido iniciado sem erro). O subshell será fechado imediatamente após o início dos trabalhos, pois não há waitcomando.

Se você deseja executar um comando com base no valor de retorno de dois ou mais processos em segundo plano simultâneos, não vejo outra maneira além de usar arquivos temporários para os valores de retorno. Isso ocorre porque waitsomente pode retornar o valor de retorno de um dos processos que ele espera. Além disso, como os processos em segundo plano devem ser executados em subshells para obter seus valores de retorno, eles não podem ser armazenados em variáveis. Você poderia fazer:

something InputA >IrrelevantA &
something InputB >IrrelevantB &

tmp1=$(mktemp)
tmp2=$(mktemp)

( somethingElse InputA >OutputA; echo $? >"$tmp1" ) &
proc1=$!

( somethingElse InputB >OutputB; echo $? >"$tmp2" ) &
proc2=$!

wait "$proc1" "$proc2"

read ret1 <"$tmp1"
read ret2 <"$tmp2"
[ "$ret1" = 0 && "ret2" = 0 ] && combine OutputA OutputB >Result

rm "$tmp1" "$tmp2"

Se você realmente não se importa com os valores retornados, basta iniciar os trabalhos normalmente e usar wait:

something InputA >IrrelevantA &
something InputB >IrrelevantB &

somethingElse InputA >OutputA &
proc1=$!

somethingElse InputB >OutputB &
proc2=$!

wait "$proc1" "$proc2"
combine OutputA OutputB >Result
Graeme
fonte
Oi, eu acho que a segunda opção seria trabalhar para mim ...
Stephen Henderson
3

Processo de substituição seria mais eficiente, especialmente se você não precisa para salvar os arquivos OutputAe OutputB, e se preocupam apenas com Result? Isso economizaria particularmente tempo, porque se você tiver uma E / S lenta ao gravar no disco, salvar os arquivos OutputAe OutputBpode ser a etapa de limitação de taxa?

combine  <(somethingElse InputA)  <(somethingElse InputB)  >  Result

A substituição do processo permite que você insira o comando em <(..here..)vez de salvar a saída em um arquivo e, em seguida, leia-o como entrada na etapa "combinar".

Se a memória é uma limitação, e o tamanho outputAe outputBmais do que a memória pode conter, ela derrotará todo o propósito?

Esperará combineaté que ambos os processos sejam concluídos antes de começar a execução?

TW Tan
fonte
Isto não é "Jeopardy"; por favor, não expresse sua resposta na forma de uma pergunta. Sério, você teve uma ideia nova e acho que é muito boa. Para responder a alguns dos seus pontos: combinecomeçará a funcionar assim que os dois somethingElsecomandos forem iniciados, mas tudo bem, porque as <(…)coisas são canalizações; então, combineserá simplesmente forçado a aguardar pelos dados, se eles ultrapassarem os somethingElseprocessos. E, por serem tubos, o tamanho não é um problema. … (Continua)
G-Man diz 'Restabelecer Monica'
(Continua) ... O único problema substantivo que tenho com sua resposta é que ele não permite testar o status de saída dos somethingElseprocessos - e não está totalmente claro se isso é importante para o solicitante. Mas, também, uma resposta não deve ser fazer perguntas como essa.
G-Man diz 'Reinstate Monica'
2

Você pode usar o waitcomando:

(echo starting & sleep 10 & wait) && echo done

Você pode ver a linha "inicial" acontecendo imediatamente e o "pronto" aguarda 10 segundos.

psusi
fonte
geralmente wait requer processos filho do mesmo shell. A espera é bem complicada lá.
precisa saber é o seguinte
1
@ mikeserv, o que você está falando? Esse é o ponto: espera por todas as crianças nesse subconjunto.
Psusi
pelos meus testes iniciais isso funciona. Eu vou experimentá-lo no grande roteiro agora
Stephen Henderson
Exatamente - filhos do mesmo shell - sub conchas. Deve funcionar para qualquer processo que não tente escapar - ou daemonize ou o que quer que seja. Isso é tudo o que eu quis dizer - desde que seus processos respeitem que os líderes do processo esperem, mas assim que um processo tentar se tornar seu próprio líder, o processo terá problemas.
precisa saber é o seguinte
0

Na verdade, demonstro exatamente como esse tipo de coisa pode ser feito em outra resposta aqui . Essa resposta foi para uma pergunta sobre como garantir que 2 logs fossem mantidos por um processo em segundo plano, então demonstrei com 10.

Script de demonstração

cat <<-\DEMO >|${s=/tmp/script} 
printf 'tty is %s\nparent pid is %s\npid is pid=%s\n' \
     "$(tty)" "$PPID" "$$"
exec 1>&2 ; nums=$(seq 0 9)
rm ${files=$(printf "/tmp/file%s\n" $nums)}
for n in $nums ; do { for f in $files ; do
    echo "Line $n" >>"$f" ; done
sleep 1 ; } ; done
#END
DEMO

Executar demonstração

s=/tmp/script ;chmod +x $s ;info="$(($s &)2>&- &)"
echo "$info" ; pid="${info##*=}" ; echo
while ps -p $pid >/dev/null ; do sleep 3 ; done
for f in /tmp/file[0-9] ; do
    printf 'path : %s\tline count : %s\n' \
        $f $(<$f wc -l)
done

Resultado:

tty is not a tty
parent pid is 1
pid is 12123

path : /tmp/file0    line count : 10
path : /tmp/file1    line count : 10
path : /tmp/file2    line count : 10
path : /tmp/file3    line count : 10
path : /tmp/file4    line count : 10
path : /tmp/file5    line count : 10
path : /tmp/file6    line count : 10
path : /tmp/file7    line count : 10
path : /tmp/file8    line count : 10
path : /tmp/file9    line count : 10

O acima demonstrado. Ele cria e executa um script chamado /tmp/script, chmodé que como executável, e executa-lo na &backgroundde um &backgrounded ( subshell ).

O script rms /tmp/file0-910 arquivos e echoesuma linha a cada segundo em todos os 10 deles. Capto alguns $infodo processo renegado e o apresento através de $(command substitution). While psrelatórios estáticos na $pidcaptura, sei que ainda é executado. sleep.Quando termina, as linhas dos 10 arquivos são contadas comwc.

Depois de chamar um processo dessa maneira, você pode fechar livremente o processo pai original e ele continuará sendo transportado - ele é efetivamente rejeitado. Isso também significa que você não pode usar o waitcomando convencional , mas aguardar pso retorno deve ser mais robusto em qualquer caso.

Vale mencionar, eu acho, que o processo é realmente chamado inicialmente $(command substitution)e printfseu o que $infoeu quero para que eu possa efetivamente controlá-lo. Mas assim que diminui sua saída do terminal exec 1>&2(que é fechada no mesmo subshell 2>&-), o processo escapa e eu tenho que esperar por isso do outro lado. É o melhor dos dois mundos, especialmente se você o usa para manipular canais de entrada, desde que possa se concentrar em todos os redirecionamentos e líderes de processos.

Tudo o resto é apenas para demonstração aqui. Tudo o que você precisa para executar este é o script principal e:

info="$(($script_path &)2>&- &)"    

NOTA: Isso só imprime no terminal exatamente o que eu queria demonstrar. Conforme observado pelo$PPID,processo, este é renegado pelo terminal e é filho direto de$PID 1.

Se você deseja executar duas delas simultaneamente e esperar por elas, basta entregar os psdois pids e esperar.

mikeserv
fonte