Bash, ¿cómo dejar que se ejecuten algunos procesos en segundo plano pero esperar otros?

11

Tengo (aún) otra wait, &, &&pregunta flujo de control ..

Digamos que tengo un script como este donde quiero hacer tanto trabajo al mismo tiempo como sea posible:

# 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

Pregunta 1: En el script, ¿ combineespera que ambos somethingElseprocesos terminen mientras ambos somethingprocesos continúan?

Pregunta 2: Si no, y sospecho que no, ¿cómo puedo combineesperar solo para ambos somethingElseprocesos mientras los somethingprocesos anteriores continúan funcionando en segundo plano?

Stephen Henderson
fuente

Respuestas:

13

En su ejemplo, el combinecomando se ejecutará tan pronto como la subshell salga (y siempre que el último proceso en segundo plano se haya iniciado sin un error). La subshell saldrá inmediatamente después de que se inicien los trabajos, ya que no hay ningún waitcomando.

Si desea ejecutar un comando basado en el valor de retorno de dos o más procesos simultáneos en segundo plano, entonces no puedo ver otra forma que no sea el uso de archivos temporales para los valores de retorno. Esto se debe a waitque solo puede devolver el valor de retorno de uno de los procesos que espera. Además, dado que los procesos en segundo plano deben ejecutarse en subcapas para obtener sus valores de retorno, no pueden almacenarse en variables. Podrías hacerlo:

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"

Si realmente no le importan los valores de retorno, puede comenzar los trabajos normalmente y usar wait:

something InputA >IrrelevantA &
something InputB >IrrelevantB &

somethingElse InputA >OutputA &
proc1=$!

somethingElse InputB >OutputB &
proc2=$!

wait "$proc1" "$proc2"
combine OutputA OutputB >Result
Graeme
fuente
Hola, creo que la segunda opción funcionaría para mí ...
Stephen Henderson
3

¿La sustitución del proceso sería más eficiente, especialmente si no necesita guardar los archivos OutputAy OutputBsolo le importa Result? ¿Esto sería particularmente un ahorro de tiempo porque si tiene una E / S lenta al escribir en el disco, guardar los archivos OutputAy OutputBpodría ser el paso de limitación de velocidad?

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

La sustitución de procesos le permite colocar el comando dentro en <(..here..)lugar de guardar el resultado en un archivo y luego leerlo como entrada en el paso "combinar".

Si la memoria es una limitación, y el tamaño outputAy outputBmás de lo que la memoria puede contener, ¿derrotará todo el propósito?

¿ combineEsperará hasta que se completen ambos procesos antes de que comience a ejecutarse?

TW Tan
fuente
Esto no es "peligro"; por favor no exprese su respuesta en forma de pregunta. En serio, se te ocurrió una idea nueva, y creo que es bastante buena. Para responder a un par de sus puntos: combinecomenzará a ejecutarse tan pronto como se somethingElsehayan iniciado los dos comandos, pero está bien, porque las <(…)cosas son tuberías; por combinelo tanto , simplemente se verá obligado a esperar datos si supera los somethingElseprocesos. Y, como son tuberías, el tamaño no es un problema. … (Continúa)
G-Man dice 'Reincorporar a Mónica'
(Continúa) ... El único problema importante que tengo con su respuesta es que no permite probar el estado de salida de los somethingElseprocesos, y no está del todo claro si eso es importante para el autor de la pregunta. Pero, además, una respuesta no debería ser hacer preguntas como esa.
G-Man dice 'Restablece a Mónica'
2

Puedes usar el waitcomando:

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

Puede ver que la línea "inicial" ocurre de inmediato, y que la "lista" espera 10 segundos.

psusi
fuente
generalmente esperar requiere procesos hijos del mismo shell. Esperar es bastante complicado allí.
mikeserv 01 de
1
@mikeserv, ¿de qué estás hablando? Ese es el punto: espera a todos los niños en esa subshell.
psusi
por mis pruebas iniciales esto funciona. Voy a probarlo en el guión grande ahora
Stephen Henderson
Exactamente - niños de la misma cáscara - sub conchas. Debería funcionar para cualquier proceso que no intente escapar, o demonizar o lo que sea. Eso es todo lo que quise decir: siempre y cuando sus procesos respeten a los líderes de proceso, la espera está bien, pero tan pronto como un proceso intente convertirse en su propio líder de proceso, la espera tendrá problemas.
mikeserv 01 de
0

De hecho, demuestro exactamente cómo se podría hacer este tipo de cosas en otra respuesta aquí . Esa respuesta fue a una pregunta sobre cómo garantizar que 2 registros se mantuvieran mediante un proceso en segundo plano, así que lo demostré con 10.

Script de demostración

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

Ejecutar demo

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

Salida:

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

Lo anterior demuestra. Construye y ejecuta un script llamado /tmp/script, chmodes como ejecutable, y lo ejecuta en el &backgroundde a &backgrounded ( subshell ).

El script rms /tmp/file0-910 archivos y echoesuna línea cada segundo en los 10 de ellos. Capturo algo $infodel proceso rechazado y lo presento a través de $(command substitution). While psinformes fijos sobre la $pidcaptura, sé que todavía se ejecuta, así que sleep.cuando se completa, se cuentan las líneas en los 10 archivos conwc.

Después de invocar un proceso de esta manera, puede cerrar libremente su proceso padre original y continuará transportando en camiones: se desautoriza efectivamente. Esto también significa que no puede utilizar el convencional waitde comandos, pero espera en ps's de retorno debe ser más robusto en cualquier caso.

Vale la pena mencionar, creo, que el proceso se llama inicialmente $(command substitution)y printfsyo $infolo deseo para poder controlarlo de manera efectiva. Pero tan pronto como deja caer su salida de terminal con exec 1>&2(que se cierra en la misma subshell con 2>&-), el proceso se escapa y tengo que esperar en el otro extremo. Un poco lo mejor de ambos mundos, especialmente si lo usa para manejar tuberías de entrada, siempre que pueda comprender todas las redirecciones y líderes de procesos.

Todo lo demás es solo para demostración aquí. Todo lo que necesita para ejecutar este es el script superior y:

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

NOTA: Esto solo imprime en la terminal exactamente lo que deseaba demostrar. Como se señaló en$PPID,este proceso, el terminal no lo reconoce y es un hijo directo de$PID 1.

Si desea ejecutar dos de estos al mismo tiempo y esperarlos, simplemente puede entregar pssus dos pids y esperar.

mikeserv
fuente