(bash) Script A, espera pelo script B, mas não pelo processo filho

9

Então eu tenho scriptA que faz:

ssh server1 -- scriptB &
ssh server2 -- scriptB &
ssh server3 -- scriptB &
wait
otherstuffhappens

ScriptB:

rsync -av /important/stuff/. remoteserver:/remote/dir/.
rsync -av /not/so/important/stuff/. remoteserver:/remote/dir/. &
exit

Meu resultado desejado é scriptA esperará que todas as instâncias de scriptB terminem antes de prosseguir, o que atualmente faz, no entanto, ele também está aguardando pelos rsyncs em segundo plano das coisas não tão importantes. Estes são arquivos maiores que eu não quero esperar.

Li através de Diferença entre nohup, disown e & e tentei combinações diferentes, mas não estou conseguindo o resultado que estou procurando.

Neste ponto, estou bastante perplexo. Qualquer ajuda seria apreciada!

    
por Apoc 27.07.2017 / 15:37

6 respostas

15

O problema aqui é que sshd aguarda o fim do arquivo no canal que está lendo o stdout do comando (não o stderr por algum motivo, pelo menos com a versão que estou testando). E o trabalho em segundo plano herda um fd nesse pipe.

Então, para contornar isso, redirecione a saída do comando background rsync para algum arquivo ou /dev/null se você não se importar com isso. Você também deve redirecionar o stderr, porque mesmo que o sshd não esteja esperando pelo canal correspondente, após sshd sair, o canal será quebrado, então rsync será eliminado se ele tentar escrever no stderr.

Então:

rsync ... > /dev/null 2>&1 &

Compare:

$ time ssh localhost 'sleep 2 &'
ssh localhost 'sleep 2 &'  0.05s user 0.00s system 2% cpu 2.365 total
$ time ssh localhost 'sleep 2 > /dev/null &'
ssh localhost 'sleep 2 > /dev/null &'  0.04s user 0.00s system 12% cpu 0.349 total

E:

$ ssh localhost '(sleep 1; ls /x; echo "$?" > out) > /dev/null &'; sleep 2; cat out
141  # ls by killed with SIGPIPE upon writing the error message
$ ssh localhost '(sleep 1; ls /x; echo "$?" > out) > /dev/null 2>&1 &'; sleep 2; cat out
2    # ls exited normally after writing the error on /dev/null instead
     # of a broken pipe
    
por 27.07.2017 / 16:46
4

Escreva um script que seja o pai de ambos, então você pode facilmente controlar ambos . Alternativamente, estabeleça um canal de comunicação entre os dois canais .

Para ignorar especificamente determinados trabalhos em segundo plano, você pode capturar o PID ( $! é o último trabalho em segundo plano PID) e wait $pid para aguardar apenas que o trabalho seja concluído.

    
por 27.07.2017 / 15:50
3

A -f flag para ssh corrige o problema. Testado em:

#!/bin/sh -e
echo $$_script__begin
( echo sleeping; sleep 2; echo slept )&
echo $$_script__end

Quando eu executo com ssh localhost ./script , ele aguarda até que slept apareça. Com o sinal -f , sai em echo $$_script__end e slept depois aparece em segundo plano após o comando ssh ter retornado.

    
por 27.07.2017 / 16:51
2

Este é um problema conhecido do servidor OpenSSH, que é descrito e discutido no bugzilla # 2071 do autor. . No bug, são propostas várias soluções alternativas no lado do OpenSSH, mas também para o script.

Se você quiser aguardar a saída dos scripts, adicione um wait antes de exit do scriptB .

Se você não se importa com a saída, use alguma variação de nohup e redirecionamento de IO para /dev/null , o que resolverá o problema da mesma forma.

    
por 27.07.2017 / 20:57
1

Você pode tentar isso. $! é a variável de shell padrão que contém o ID do processo do processo / pipeline de segundo plano executado mais recentemente.

command1 &
lpid1=$!

command2 &
lpid2=$!

command3 &
lpid=$!

wait $lpid1  # waits for only the process with PID lpid1  to complete. 

Você precisa utilizar isso de acordo com seu script usando export variable etc

    
por 27.07.2017 / 15:52
1

B só precisa esperar pelos próprios processos em segundo plano:

rsync -av /important/stuff/. remoteserver:/remote/dir/.
rsync -av /not/so/important/stuff/. remoteserver:/remote/dir/. &
wait
exit
    
por 27.07.2017 / 20:33