¿Cómo esperar en un script bash varios subprocesos generados desde ese script para finalizar y devolver el código de salida! = 0 cuando alguno de los subprocesos termina con el código! = 0?
Script simple:
#!/bin/bash
for i in `seq 0 9`; do
doCalculations $i &
done
wait
El script anterior esperará a los 10 subprocesos generados, pero siempre dará el estado de salida 0 (ver help wait
). ¿Cómo puedo modificar este script para que descubra los estados de salida de los subprocesos generados y devuelva el código de salida 1 cuando alguno de los subprocesos termina con el código! = 0?
¿Hay alguna solución mejor para eso que recolectar los PID de los subprocesos, esperarlos en orden y sumar los estados de salida?
wait -n
, disponible en bash moderno para regresar solo cuando se complete el primer / siguiente comando.wait -n
tiene un pequeño problema: si no quedan trabajos secundarios restantes (también conocido como condición de carrera), devuelve un estado de salida distinto de cero (fallo) que puede ser indistinguible de un proceso secundario fallido.Respuestas:
wait
también (opcionalmente) toma el PID del proceso para esperar, y con $! obtienes el PID del último comando lanzado en segundo plano. Modifique el bucle para almacenar el PID de cada subproceso generado en una matriz, y luego vuelva a bucle esperando cada PID.fuente
for i in $n_procs; do ./procs[${i}] & ; pids[${i}]=$!; done; for pid in ${pids[*]}; do wait $pid; done;
¿verdad?http://jeremy.zawodny.com/blog/archives/010717.html :
fuente
jobs -p
está dando PID de subprocesos que están en estado de ejecución. Omitirá un proceso si el proceso finaliza antes de quejobs -p
se llame. Entonces, si alguno de los subprocesos termina antesjobs -p
, el estado de salida de ese proceso se perderá.jobs -p
no está dando PID de subprocesos, sino GPID . La lógica de espera parece funcionar de todos modos, siempre espera en el grupo si dicho grupo existe y pide si no, pero es bueno tener en cuenta ... especialmente si uno se basa en esto e incorpora algo como enviar mensajes al subproceso en el que caso la sintaxis es diferente dependiendo de si tiene PID o GPID .. es decir,kill -- -$GPID
vskill $PID
Aquí hay un ejemplo simple de uso
wait
.Ejecute algunos procesos:
Luego espere con el
wait
comando:O simplemente
wait
(sin argumentos) para todos.Esto esperará a que se completen todos los trabajos en segundo plano.
Si
-n
se proporciona la opción, espera a que termine el siguiente trabajo y devuelve su estado de salida.Ver:
help wait
yhelp jobs
para la sintaxis.Sin embargo, la desventaja es que esto solo devolverá el estado de la última ID, por lo que debe verificar el estado de cada subproceso y almacenarlo en la variable.
O haga su función de cálculo para crear algún archivo en caso de falla (vacío o con registro de fallas), luego verifique ese archivo si existe, por ejemplo
fuente
sleep 20 && true
ysleep 20 && false
, es decir, reemplazar aquellos con sus funciones. Para comprender&&
y||
ejecutarman bash
y escribir '/' (buscar), luego '^ * Listas' (una expresión regular) y luego ingresar: man se desplazará hacia abajo hasta la descripción de&&
y||
||
STDERR falle también.wait
Si tiene GNU Parallel instalado, puede hacer:
GNU Parallel le dará el código de salida:
0: todos los trabajos se ejecutaron sin error.
1-253 - Algunos de los trabajos fallaron. El estado de salida da el número de trabajos fallidos
254: más de 253 trabajos fallaron.
255 - Otro error.
Mire los videos de introducción para obtener más información: http://pi.dk/1
fuente
doCalculations
hay una función definida en ese mismo script (aunque el OP no estaba claro sobre este requisito). Cuando lo intento,parallel
dice/bin/bash: doCalculations: command not found
(dice esto 10 veces para elseq 0 9
ejemplo anterior). Vea aquí para una solución alternativa.xargs
tiene cierta capacidad para iniciar trabajos en paralelo a través de la-P
opción. A partir de aquí :export -f doCalculations ; seq 0 9 |xargs -P 0 -n 1 -I{} bash -c "doCalculations {}"
. Las limitaciones dexargs
se enumeran en la página del manual paraparallel
.doCalculations
basa en otras variables de entorno internas de script (personalizadasPATH
, etc.), probablemente necesiten ser editadas explícitamenteexport
antes de iniciarseparallel
.wget -O - pi.dk/3 | sh
no obtendrá confusiones. Si su empaquetador lo ha estropeado, le animo a que plantee el problema con su empaquetador. Las variables y funciones deben exportarse (export -f) para que GNU Parallel las vea (verman parallel
: gnu.org/software/parallel/… )¿Qué tal simplemente:
Actualizar:
Como señalaron varios comentaristas, lo anterior espera a que se completen todos los procesos antes de continuar, pero no sale y falla si uno de ellos falla, se puede hacer con la siguiente modificación sugerida por @Bryan, @SamBrightman y otros :
fuente
for pid in $pids; do wait $pid; done
help wait
: con múltiples IDwait
devuelve el código de salida del último solo, como @ vlad-frolov dijo anteriormente.wait
pasa si un proceso determinado sale antes de que se llame al correspondiente ? Resulta que esto no es un problema: siwait
en un proceso que ya ha salido,wait
saldrá inmediatamente con el estado del proceso ya salido. (¡Gracias,bash
autores!)Esto es lo que se me ocurrió hasta ahora. Me gustaría ver cómo interrumpir el comando de suspensión si un niño termina, para que uno no tenga que sintonizar
WAITALL_DELAY
su uso.fuente
Para paralelizar esto ...
Traducirlo a esto ...
--max-procs
función de la cantidad de paralelismo que desee (0
significa "todo a la vez").xargs
, pero no siempre se instala por defecto.for
bucle no es estrictamente necesario en este ejemplo, yaecho $i
que básicamente solo está regenerando la salida de$(whatever_list
). Solo creo que el uso de lafor
palabra clave hace que sea un poco más fácil ver lo que está sucediendo.Aquí hay un ejemplo de trabajo simplificado ...
fuente
--max-procs
: ¿Cómo obtener el número de CPU / núcleos en Linux desde la línea de comandos?No creo que sea posible con la funcionalidad integrada de Bash.
Usted puede recibir una notificación cuando un niño sale:
Sin embargo, no hay una forma aparente de obtener el estado de salida del niño en el controlador de señal.
Obtener ese estado secundario suele ser el trabajo de la
wait
familia de funciones en las API POSIX de nivel inferior. Desafortunadamente, el soporte de Bash para eso es limitado: puede esperar un proceso secundario específico (y obtener su estado de salida) o puede esperar a todos ellos y siempre obtener un resultado 0.Lo que parece imposible de hacer es el equivalente de
waitpid(-1)
, que bloquea hasta que regrese cualquier proceso hijo.fuente
Veo muchos buenos ejemplos enumerados aquí, también quería lanzar el mío.
Utilizo algo muy similar para iniciar / detener servidores / servicios en paralelo y verificar cada estado de salida. Funciona muy bien para mi. ¡Espero que esto ayude a alguien!
fuente
trap "kill 0" EXIT
Esto es algo que uso:
fuente
El siguiente código esperará la finalización de todos los cálculos y devolverá el estado de salida 1 si falla alguno de los cálculos .
fuente
Simplemente almacene los resultados fuera del shell, por ejemplo, en un archivo.
fuente
Aquí está mi versión que funciona para múltiples pids, registra advertencias si la ejecución lleva demasiado tiempo y detiene los subprocesos si la ejecución lleva más tiempo que un valor dado.
Ejemplo, espere a que finalicen los tres procesos, registre una advertencia si la ejecución demora más de 5 segundos, detenga todos los procesos si la ejecución demora más de 120 segundos. No salga del programa por fallas.
fuente
Si tiene disponible bash 4.2 o posterior, lo siguiente podría serle útil. Utiliza matrices asociativas para almacenar nombres de tareas y su "código", así como nombres de tareas y sus pids. También he incorporado un método simple de limitación de velocidad que puede ser útil si sus tareas consumen mucho tiempo de CPU o E / S y desea limitar la cantidad de tareas simultáneas.
El script inicia todas las tareas en el primer bucle y consume los resultados en el segundo.
Esto es un poco exagerado para casos simples, pero permite cosas bastante ordenadas. Por ejemplo, uno puede almacenar mensajes de error para cada tarea en otra matriz asociativa e imprimirlos después de que todo se haya establecido.
fuente
Acabo de modificar una secuencia de comandos en segundo plano y paralelizar un proceso.
Experimenté un poco (en Solaris con bash y ksh) y descubrí que 'esperar' genera el estado de salida si no es cero, o una lista de trabajos que devuelven una salida distinta de cero cuando no se proporciona ningún argumento PID. P.ej
Golpetazo:
Ksh:
Este resultado se escribe en stderr, por lo que una solución simple para el ejemplo de OP podría ser:
Mientras esto:
también devolverá un conteo pero sin el archivo tmp. Esto también se puede usar de esta manera, por ejemplo:
Pero esto no es mucho más útil que el archivo tmp IMO. No pude encontrar una manera útil de evitar el archivo tmp y al mismo tiempo evitar ejecutar la "espera" en una subshell, que no funcionará en absoluto.
fuente
He intentado esto y he combinado todas las mejores partes de los otros ejemplos aquí. Este script ejecutará la
checkpids
función cuando salga cualquier proceso en segundo plano y generará el estado de salida sin recurrir al sondeo.fuente
set -m
le permite usar fg & bg en un scriptfg
, además de poner el último proceso en primer plano, tiene el mismo estado de salida que el proceso en primer planowhile fg
dejará de repetirse cuandofg
salga con un estado de salida distinto de cerodesafortunadamente, esto no manejará el caso cuando un proceso en segundo plano salga con un estado de salida distinto de cero. (el bucle no terminará inmediatamente. esperará a que se completen los procesos anteriores).
fuente
Ya hay muchas respuestas aquí, pero me sorprende que nadie parezca haber sugerido usar matrices ... Entonces, esto es lo que hice: esto podría ser útil para algunos en el futuro.
fuente
¡Esto funciona, debería ser tan bueno, si no mejor, que la respuesta de @ HoverHell!
y, por supuesto, he inmortalizado este script, en un proyecto NPM que le permite ejecutar comandos bash en paralelo, útil para probar:
https://github.com/ORESoftware/generic-subshell
fuente
trap $? $$
parece conjunto de códigos de salida a 0 y el PID de cáscara del golpe de ejecución actual, cada vez que para míLa trampa es tu amiga. Puede atrapar ERR en muchos sistemas. Puede atrapar EXIT, o en DEBUG para realizar una pieza de código después de cada comando.
Esto además de todas las señales estándar.
fuente
La
set -e
parte superior hace que su script se detenga en caso de falla.expect
volverá1
si falla cualquier subtrabajo.fuente
Exactamente para este propósito escribí una
bash
función llamada:for
.Nota :
:for
no solo conserva y devuelve el código de salida de la función anómala, sino que también termina todas las instancias en ejecución paralelas. Lo que podría no ser necesario en este caso.uso
for.sh
:Referencias
fuente
Utilicé esto recientemente (gracias a Alnitak):
A partir de ahí, se puede extrapolar fácilmente y tener un activador (tocar un archivo, enviar una señal) y cambiar los criterios de conteo (contar archivos tocados o lo que sea) para responder a ese activador. O si solo desea 'cualquier' rc distinto de cero, simplemente elimine el bloqueo de save_status.
fuente
Necesitaba esto, pero el proceso de destino no era hijo del shell actual, en cuyo caso
wait $PID
no funciona. En su lugar encontré la siguiente alternativa:Eso se basa en la presencia de procfs , que pueden no estar disponibles (Mac no lo proporciona, por ejemplo). Entonces, para la portabilidad, podría usar esto en su lugar:
fuente
La captura de la señal CHLD puede no funcionar porque puede perder algunas señales si llegan simultáneamente.
fuente
La solución para esperar varios subprocesos y salir cuando alguno de ellos sale con un código de estado distinto de cero es mediante 'wait -n'
el código de estado '127' es para un proceso no existente, lo que significa que el niño podría haber salido.
fuente
Espere todos los trabajos y devuelva el código de salida del último trabajo fallido. A diferencia de las soluciones anteriores, esto no requiere guardar pid. Solo bg lejos, y espera.
fuente
Puede haber un caso en el que el proceso se complete antes de esperar el proceso. Si activamos esperar un proceso que ya ha finalizado, activará un error como pid no es un hijo de este shell. Para evitar tales casos, la siguiente función se puede utilizar para determinar si el proceso está completo o no:
fuente
Creo que la forma más directa de ejecutar trabajos en paralelo y verificar el estado es utilizando archivos temporales. Ya hay un par de respuestas similares (por ejemplo, Nietzche-jou y mug896).
El código anterior no es seguro para subprocesos. Si le preocupa que el código anterior se ejecute al mismo tiempo que él mismo, es mejor usar un nombre de archivo más exclusivo, como fail. $$. La última línea es cumplir el requisito: "devolver el código de salida 1 cuando cualquiera de los subprocesos termina con el código! = 0?" Lancé un requisito adicional allí para limpiar. Puede haber sido más claro escribirlo así:
Aquí hay un fragmento similar para recopilar resultados de múltiples trabajos: creo un directorio temporal, cuento los resultados de todas las subtareas en un archivo separado y luego los vuelco para su revisión. Esto realmente no coincide con la pregunta: lo estoy agregando como un bono:
fuente
Casi caigo en la trampa de usar
jobs -p
para recolectar PID, que no funciona si el niño ya ha salido, como se muestra en el script a continuación. La solución que elegí fue simplemente llamarwait -n
N veces, donde N es el número de hijos que tengo, lo que resulta que sé determinista.Salida:
fuente