(bash) Script A, espere el script B, pero no su proceso hijo

9

Entonces tengo scriptA que hace:

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

ScriptB hace:

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

Mi resultado deseado es scriptA esperará a que finalicen todas las instancias de scriptB antes de continuar, lo que actualmente hace, sin embargo, también está esperando los rsyncs de fondo de las cosas no tan importantes. Estos son archivos más grandes que no quiero esperar.

He leído Diferencia entre nohup, disown y & e intentado diferentes combinaciones, pero no obtengo el resultado que estaba buscando.

En este punto estoy bastante perplejo. ¡Cualquier ayuda sería apreciada!

Apoc
fuente

Respuestas:

15

El problema aquí es que sshdespera el final del archivo en la tubería en la que está leyendo el stdout del comando (no el stderr por alguna razón, al menos con la versión en la que estoy probando). Y el trabajo en segundo plano hereda un fd a esa tubería.

Entonces, para evitar eso, redirija la salida de ese rsynccomando de fondo a algún archivo o /dev/nullsi no le importa. También debe redirigir stderr, porque incluso si sshd no está esperando la tubería correspondiente, después de las sshdsalidas, la tubería se romperá, por rsynclo que sería eliminada si intenta escribir en stderr.

Entonces:

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

Y:

$ 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
Stéphane Chazelas
fuente
3

La -fbandera para sshsolucionar el problema. Probado en:

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

Cuando lo ejecuto ssh localhost ./script, espera hasta que sleptaparezca. Con la -fbandera, sale echo $$_script__endy sleptluego aparece en segundo plano después de que el sshcomando ha regresado.

PSkocik
fuente
2

Este es un problema conocido del servidor OpenSSH, que se describe y discute en el bugzilla ascendente # 2071 . En el error, se proponen varias soluciones alternativas en el lado de OpenSSH, pero también para el script.

Si desea esperar la salida de los scripts, también debe agregar un waitantes exitdel scriptB.

Si no le importa la salida, use alguna variación nohupy redirección de E / S /dev/null, lo que resolverá el problema de la misma manera.

Jakuje
fuente
1

Puedes probar esto. $!es la variable de shell predeterminada que contiene el ID de proceso de la tubería / proceso en segundo plano ejecutado más recientemente.

command1 &
lpid1=$!

command2 &
lpid2=$!

command3 &
lpid=$!

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

Debe utilizar esto según su script utilizando exportvariables, etc.

Kaushik Nayak
fuente
1

B solo necesita esperar sus propios procesos en segundo plano:

rsync -av /important/stuff/. remoteserver:/remote/dir/.
rsync -av /not/so/important/stuff/. remoteserver:/remote/dir/. &
wait
exit
Jared Lovell
fuente
En ese punto, es mejor que no ejecutes el segundo rsync en segundo plano y evites usarlo por waitcompleto. Aunque supongo que lo que el OP quería hacer era ejecutar ambos rsyncprocesos en paralelo, lo que significaría ponerlos en segundo plano (con &) y luego usarlos wait. En cualquier caso, estoy de acuerdo en que esta es la forma más sencilla de solucionar el problema y es la que elegiría en función de la información de la pregunta.
David Z