una tomar prime pantalla mano hacer con como celular captura shell pipe

tomar - Códigos de error de captura en un tubo de shell



como tomar captura de pantalla (4)

Desafortunadamente, la respuesta de Johnathan requiere archivos temporales y las respuestas de Michel e Imron requieren bash (aunque esta pregunta está etiquetada como shell). Como ya se señaló por otros, no es posible abortar la tubería antes de que se inicien procesos posteriores. Todos los procesos se inician a la vez y, por lo tanto, todos se ejecutarán antes de que se puedan comunicar los errores. Pero el título de la pregunta también preguntaba sobre los códigos de error. Estos pueden recuperarse e investigarse después de que la tubería termine para determinar si alguno de los procesos involucrados falló.

Aquí hay una solución que detecta todos los errores en la tubería y no solo los errores del último componente. Así que esto es como el error de bash de bash, simplemente más poderoso en el sentido de que puedes recuperar todos los códigos de error.

res=$( (./a 2>&1 || echo "1st failed with $?" >&2) | (./b 2>&1 || echo "2nd failed with $?" >&2) | (./c 2>&1 || echo "3rd failed with $?" >&2) > /dev/null 2>&1) if [ -n "$res" ]; then echo pipe failed fi

Para detectar si algo falló, un comando de echo imprime en un error estándar en caso de que falle algún comando. Luego, la salida de error estándar combinada se guarda en $res y se investiga más adelante. Este es también el motivo por el error estándar de todos los procesos se redirige a la salida estándar. También puede enviar ese resultado a /dev/null o dejarlo como otro indicador de que algo salió mal. Puede reemplazar el último redireccionamiento a /dev/null con un archivo si está listo para almacenar la salida del último comando en cualquier lugar.

Para jugar más con este constructo y convencerse de que esto realmente hace lo que debería, reemplacé ./a , ./b y ./c por ./c que ejecutan echo , cat y exit . Puede usar esto para verificar que esta construcción realmente reenvía todo el resultado de un proceso a otro y que los códigos de error se graben correctamente.

res=$( (sh -c "echo 1st out; exit 0" 2>&1 || echo "1st failed with $?" >&2) | (sh -c "cat; echo 2nd out; exit 0" 2>&1 || echo "2nd failed with $?" >&2) | (sh -c "echo start; cat; echo end; exit 0" 2>&1 || echo "3rd failed with $?" >&2) > /dev/null 2>&1) if [ -n "$res" ]; then echo pipe failed fi

Actualmente tengo un script que hace algo así como

./a | ./b | ./c

Quiero modificarlo para que, si alguno de a, boc sale con un código de error, imprima un mensaje de error y detenga en lugar de enviar el resultado incorrecto.

¿Cuál sería la forma más simple / limpia de hacerlo?


En bash , puede usar set -e y set -o pipefail al principio de su archivo. Un comando posterior ./a | ./b | ./c ./a | ./b | ./c ./a | ./b | ./c fallará cuando cualquiera de los tres scripts falle. El código de retorno será el código de retorno del primer script fallido.

Tenga en cuenta que pipefail no está disponible en sh estándar.


Si realmente no desea que el segundo comando continúe hasta que se sabe que el primero es exitoso, entonces probablemente necesite usar archivos temporales. La versión simple de eso es:

tmp=${TMPDIR:-/tmp}/mine.$$ if ./a > $tmp.1 then if ./b <$tmp.1 >$tmp.2 then if ./c <$tmp.2 then : OK else echo "./c failed" 1>&2 fi else echo "./b failed" 1>&2 fi else echo "./a failed" 1>&2 fi rm -f $tmp.[12]

La redirección ''1> y 2'' también se puede abreviar ''> & 2''; sin embargo, una versión anterior del shell MKS manejó mal la redirección de errores sin el ''1'' anterior, así que he usado esa notación inequívoca para la confiabilidad por edades.

Esto filtra archivos si interrumpes algo. La programación a prueba de bombas (más o menos) usa:

tmp=${TMPDIR:-/tmp}/mine.$$ trap ''rm -f $tmp.[12]; exit 1'' 0 1 2 3 13 15 ...if statement as before... rm -f $tmp.[12] trap 0 1 2 3 13 15

La primera línea de captura dice ''ejecuta los comandos'' rm -f $tmp.[12]; exit 1 rm -f $tmp.[12]; exit 1 ''cuando se produce cualquiera de las señales 1 SIGHUP, 2 SIGINT, 3 SIGQUIT, 13 SIGPIPE o 15 SIGTERM, o 0 (cuando el shell se cierra por cualquier motivo). Si está escribiendo un script de shell, la captura final solo necesita eliminar el trap en 0, que es la trampa de salida del shell (puede dejar las otras señales en su lugar ya que el proceso está a punto de terminar de todos modos).

En la tubería original, es factible que ''c'' lea los datos de ''b'' antes de que ''a'' haya terminado; esto generalmente es deseable (por ejemplo, le da trabajo a varios núcleos para hacerlo). Si ''b'' es una fase ''ordenar'', entonces esto no se aplicará - ''b'' tiene que ver toda su entrada antes de que pueda generar cualquiera de sus resultados.

Si quiere detectar qué comando (s) falla, puede usar:

(./a || echo "./a exited with $?" 1>&2) | (./b || echo "./b exited with $?" 1>&2) | (./c || echo "./c exited with $?" 1>&2)

Esto es simple y simétrico: es trivial extender a una tubería de 4 partes o N partes.

La simple experimentación con ''set-e'' no ayudó.


También puede verificar la ${PIPESTATUS[]} después de la ejecución completa, por ejemplo, si ejecuta:

./a | ./b | ./c

Entonces ${PIPESTATUS} será una matriz de códigos de error de cada comando en la tubería, por lo que si el comando del medio fallara, echo ${PIPESTATUS[@]} contendría algo así como:

0 1 0

y algo así se ejecuta después del comando:

test ${PIPESTATUS[0]} -eq 0 -a ${PIPESTATUS[1]} -eq 0 -a ${PIPESTATUS[2]} -eq 0

le permitirá verificar que todos los comandos en la tubería tuvieron éxito.