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


9

Então, eu tenho scriptA que faz:

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

ScriptB faz:

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

Meu resultado desejado é o scriptA, aguardando o término de todas as instâncias do scriptB antes de prosseguir, o que ocorre atualmente, mas também aguardando os rsyncs em segundo plano das coisas não tão importantes. Esses são arquivos maiores que eu não quero esperar.

Eu li a diferença entre nohup, renown e & e tentei combinações diferentes, mas não estou obtendo o resultado que estou procurando.

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

Respostas:


15

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

Portanto, para contornar isso, redirecione a saída desse rsynccomando em segundo plano para algum arquivo ou /dev/nullse você não se importa. Você também deve redirecionar o stderr, porque, mesmo que o sshd não esteja aguardando o pipe correspondente, após as sshdsaídas, o pipe será quebrado e, portanto rsync, será eliminado se tentar gravar no stderr.

Assim:

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

Comparar:

$ 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


3

O -fsinalizador para sshcorrigir o problema. Testado em:

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

Quando eu corro ssh localhost ./script, aguarda até sleptaparecer. Com o -fsinalizador, ele sai em echo $$_script__ende sleptdepois aparece em segundo plano após o sshretorno do comando.


2

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

Se você quiser aguardar a saída dos scripts, adicione um waitantes exitdo script scriptBtambém.

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


1

Você pode tentar isso. $!é uma variável de shell padrão que contém o ID do processo / pipeline / processo em 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 o seu script usando a exportvariável etc


1

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

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

Nesse ponto, você pode simplesmente não executar o segundo rsync em segundo plano e evitar o uso waitcompleto. Embora eu esteja adivinhando o que o OP pretendia fazer era executar os dois rsyncprocessos em paralelo, o que significaria colocar os dois em segundo plano (com &) e depois usá -los wait. De qualquer forma, concordo que essa é a maneira mais direta de corrigir o problema e que eu escolheria com base nas informações da pergunta.
David Z
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.