2012-01-23 12 views
22

Estoy usando un conducto de varios comandos en bash. ¿Hay alguna manera de configurar bash para terminar todos los comandos en toda la canalización inmediatamente si uno de los comandos falla?Canalizaciones Shell: salga inmediatamente cuando falle un comando

En mi caso, el primer comando, digamos command1, se ejecuta durante un tiempo hasta que produce algún resultado. Puede sustituir command1 por (sleep 5 && echo "Hello"), por ejemplo.

Ahora, command1 | false falla después de 5 segundos pero no de inmediato.

Este comportamiento parece tener algo que ver con la cantidad de salida que produce el comando. Por ejemplo, find/| false regresa inmediatamente.

En general, me pregunto por qué bash se comporta así. ¿Alguien puede imaginar alguna situación donde sea útil que el código como command1 | non-existing-command no salga de inmediato?

PD: El uso de archivos temporales no es una opción para mí, ya que los resultados intermedios que uso son demasiado grandes para ser almacenados.

PPS: Ni set -e ni set -o pipefail parecen influir en este fenómeno.

+1

Esta pregunta se adapta mejor a http://unix.stackexchange.com. Probablemente obtendrás una buena respuesta allí. – dogbane

Respuesta

16

La documentación fiesta dice en su section about pipelines:

Cada comando en una tubería se ejecuta en su propio subnivel [...]

"En su propia subnivel" significa que un nuevo se genera el proceso bash, que luego ejecuta el comando real. Cada subshell se inicia correctamente, incluso cuando determina inmediatamente que no existe el comando que se le pide que ejecute.

Esto explica por qué toda la tubería se puede configurar con éxito incluso cuando uno de los comandos es absurdo. Bash no comprueba si se puede ejecutar cada comando, sino que lo delega a las subcapas. Eso también explica por qué, por ejemplo, el comando nonexisting-command | touch hello lanzará un error de "comando no encontrado", pero se creará el archivo hello.

En la misma sección, también dice:

Los shell espera que todos los comandos de la tubería para terminar antes de devolver un valor.

En sleep 5 | nonexisting-command, como A.H. señaló, el sleep 5 termina después de 5 segundos, no inmediatamente, por lo tanto, la carcasa también esperar 5   segundos.

No sé por qué la implementación se realizó de esta manera. En casos como el tuyo, el comportamiento seguramente no es el esperado.

De todos modos, un poco fea solución es utilizar FIFO:

mkfifo myfifo 
./long-running-script.sh > myfifo & 
whoops-a-typo < myfifo 

Aquí, el long-running-script.sh se inicia y luego los guiones falla inmediatamente en la siguiente línea. Utilizando varios FIFOs, esto podría extenderse a tuberías con más de dos comandos.

4

sleep 5 no produce ninguna salida hasta que termina, mientras que find / produce inmediatamente salida que bash intenta canalizar a false.

+0

Cierto, la pregunta es buena pero tiene ejemplos mal elegidos. –

+0

@Jaypal: estoy de acuerdo, que el ejemplo podría ser engañoso. Edité la publicación y espero que esté más clara ahora. – Tobi

+0

@Dan: Sí, pero esto realmente no responde mi pregunta. Quiero saber si puedo hacer que bash termine todos los comandos en una tubería una vez que falla un comando. – Tobi

2

find/|false falla más rápido debido a que la primera llamada write(2) sistema de find falla con el error EPIPE (Tubo roto). Esto se debe a que false ya ha finalizado y, por lo tanto, la tubería entre estos dos comandos ya se ha cerrado en un lado.

Si find ignorara ese error (podría hacerlo en teoría) también "fallaría lentamente".

(sleep 5 && echo "Hello") | false es "fail slow", porque la primera parte, sleep, no "prueba" la tubería escribiendo en ella. Después de 5 segundos, el echo también obtiene el error EPIPE. Si este error termina la primera parte en este caso o no, no es importante para la pregunta.

3

El primer programa no sabe si el segundo finaliza o no hasta que intenta escribir una fecha en el conducto. En caso de que el segundo finalice, el primero recibe el SIGPIPE que generalmente causa la salida inmediata.

Puede forzar a la primera línea de salida para ser conducido inmediatamente después de mirar fijamente, como esto:

(sleep 0.1; echo; command1) | command2 

Este 100ms sueño se pretende que esperar hasta la posible salida comando2 justo después de comenzar. Por supuesto, si command2 sale después de 2 segundos, y command1 permanecerá en silencio durante 60 segundos, el comando de shell completo regresará solo después de 60.1 segundos.

Cuestiones relacionadas