Bash, como deixar alguns processos de segundo plano rodarem, mas esperar pelos outros?

10

Eu tenho (ainda) outra pergunta de fluxo de controle wait , & , && .

Digamos que eu tenha um script como este, no qual eu quero trabalhar o máximo 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, combine espera que os processos somethingElse sejam concluídos enquanto os processos something continuam?

Pergunta 2: Se não - e suspeito que não -, como faço para que combine aguarde apenas os processos somethingElse , enquanto something processa acima, continue trabalhando em segundo plano? / p>     

por Stephen Henderson 31.03.2014 / 23:05

4 respostas

12

No seu exemplo, o comando combine será executado assim que a sub-ssh sair (e desde que o último processo de background tenha sido iniciado sem um erro). O subshell sairá imediatamente após os jobs serem iniciados, pois não há comando wait .

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 senão usar arquivos temporários para os valores de retorno. Isso ocorre porque wait pode retornar apenas o valor de retorno de um dos processos que ele aguarda. 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 de retorno, 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
    
por 01.04.2014 / 00:25
3

O processo de substituição seria mais eficiente, especialmente se você não precisasse salvar os arquivos OutputA e OutputB e se importasse apenas com Result ? Isso seria particularmente útil para economizar tempo, porque se você tiver uma E / S lenta ao gravar em disco, salvar os arquivos OutputA e OutputB pode ser a etapa limitadora da taxa?

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

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

Se a memória é uma limitação, e o tamanho de outputA e outputB mais do que a memória pode suportar, ela derrotará todo o propósito?

combine aguardará até que ambos os processos sejam concluídos antes de começar a ser executado?

    
por 15.03.2015 / 04:59
2

Você pode usar o comando wait :

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

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

    
por 01.04.2014 / 00:25
0
Eu realmente demonstro exatamente como esse tipo de coisa pode ser feito em outra resposta. -background-nohoup / 121380 # 121380 "> aqui . Essa resposta foi para uma pergunta sobre a garantia de que dois logs foram mantidos por um processo em segundo plano, então eu demonstrei isso 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

Execução da 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

Saída:

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 demonstra. Ele cria e executa um script chamado /tmp/script , chmod 's como executável, e o executa no &background de um &backgrounded ( subshell ) .

O script rms /tmp/file0-9 10 arquivos e echoes uma linha a cada segundo em todos os 10 deles. Eu capturei alguns $info do processo de renome e o apresente via $(command substitution). While ps ainda relata o $pid que eu capturei, eu sei ele ainda roda, então eu sleep. Quando ele é concluído, as linhas em todos os 10 arquivos são contadas com wc.

Depois de invocar um processo dessa maneira, você pode fechar livremente o processo original do pai e ele continuará no transporte - ele é efetivamente deserdado. Isso também significa que você não pode usar o comando convencional wait , mas aguardar o retorno do ps deve ser mais robusto em qualquer caso.

Vale a pena mencionar que, na verdade, o processo é chamado inicialmente em $(command substitution) e printfs me em $info Eu quero que eu possa efetivamente controlá-lo. Mas, assim que a saída do terminal é descartada com exec 1>&2 (que é fechado no mesmo subshell com 2>&- ), o processo escapa e eu tenho que esperar por ele na outra ponta. O melhor dos dois mundos, especialmente se você usá-lo para manipular os canos de entrada, contanto que você possa envolver todos os redirecionamentos e líderes de processo.

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. Como observado pelo $PPID, , este processo é rejeitado pelo terminal e é um filho direto de $PID 1.

Se você quisesse executar dois destes ao mesmo tempo e esperar por eles, poderia entregar ps ambos os pids e esperar.

    
por 01.04.2014 / 20:19