así que quería aportar una respuesta como de Lesmana, pero creo que la mía es tal vez un poco más simple y poco más ventajosa solución de Bourne-Shell pura:
# You want to pipe command1 through command2:
exec 4>&1
exitstatus=`{ { command1; printf $? 1>&3; } | command2 1>&4; } 3>&1`
# $exitstatus now has command1's exit status.
Creo que esto se explica mejor desde el interior out - command1 ejecutará e imprimirá su salida normal en stdout (descriptor de archivo 1), luego una vez hecho, printf ejecutará e imprimirá el código de salida de icommand1 en su stdout, pero ese stdout se redirigirá al descriptor de archivo 3.
Mientras command1 se está ejecutando, su stdout se canaliza a command2 (la salida de printf nunca llega a command2 beca use lo enviamos al descriptor de archivo 3 en lugar de 1, que es lo que lee la tubería). Luego redirigimos la salida de command2 al descriptor de archivo 4, para que también permanezca fuera del descriptor de archivo 1 - porque queremos el descriptor de archivo 1 gratis un poco más tarde, porque llevaremos la salida printf en el descriptor 3 de archivo al descriptor de archivo 1 - porque eso es lo que capturará el comando (los backticks), y eso es lo que se colocará en la variable.
El último truco de la magia es que el primer exec 4>&1
lo hicimos como un comando separado: abre el descriptor de archivo 4 como una copia de la salida estándar del shell externo.La sustitución de comandos capturará lo que está escrito en la salida estándar desde la perspectiva de los comandos dentro de ella, pero como la salida de command2 va a presentar el descriptor 4 en lo que respecta a la sustitución de comandos, la sustitución de comandos no lo captura, aunque una vez "sale" de la sustitución del comando, todavía va al descriptor de archivo general del script 1.
(El tiene que ser un comando separado porque a muchos objetos comunes no les gusta cuando intentas escribir en un descriptor de archivo dentro de una sustitución de comando, que se abre en el comando "externo" que está usando la sustitución. Así que esta es la manera más simple de hacerlo).
Puedes verlo de una manera menos técnica y más lúdica camino, como si las salidas de los comandos se saltaran entre sí: command1 pipes a command2, la salida de printf salta sobre el comando 2 para que command2 no la atrape, y luego la salida del comando 2 salta por encima y fuera de la sustitución de comando como printf aterriza justo a tiempo para ser capturado por la sustitución de modo que termine en la variable, y la salida de command2 se transmite de manera feliz a la salida estándar, como en una tubería normal.
Además, según tengo entendido, $?
aún contendrá el código de retorno del segundo comando en la tubería, porque las asignaciones de variables, sustituciones de comandos y comandos compuestos son efectivamente transparentes para el código de retorno del comando dentro de ellos, por lo tanto, el estado de retorno de command2 debería propagarse; esto, y no tener que definir una función adicional, es la razón por la que creo que podría ser una solución algo mejor que la propuesta por lesmana.
por las advertencias Lesmana menciona, es posible que comando1 en algún momento terminar usando descriptores de fichero 3 o 4, de modo que sea más robusto, que haría:
exec 4>&1
exitstatus=`{ { command1 3>&-; printf $? 1>&3; } 4>&- | command2 1>&4; } 3>&1`
exec 4>&-
Nota que utilizo comandos compuestos en mi ejemplo, pero subniveles (usando ()
en lugar de { }
también funcionará, aunque tal vez puede ser menos eficiente.)
comandos heredan descriptores de archivo del proceso que les pone en marcha, por lo que toda la segunda línea heredará descriptor de archivo de cuatro, y el comando compuesto seguido por 3>&1
heredará el descriptor de archivo tres. Entonces, el 4>&-
se asegura de que el comando compuesto interno no herede el descriptor de archivo cuatro, y el 3>&-
no heredará el descriptor de archivo tres, por lo que command1 obtiene un entorno más limpio y más estándar. También puede mover el 4>&-
interno junto al 3>&-
, pero me imagino por qué no solo limita su alcance tanto como sea posible.
No estoy seguro de la frecuencia con que las cosas usan el descriptor de archivo tres y cuatro directamente - Creo que la mayoría de los programas usan syscalls que devuelven descriptores de archivos no utilizados en el momento, pero a veces escriben códigos en el descriptor de archivo 3 directamente, supongo (podría imaginar un programa que verifica un descriptor de archivo para ver si está abierto, y si lo está usando, o si se comporta de manera diferente si no lo está). Por lo tanto, es probable que lo último sea mejor tenerlo en cuenta y usarlo para casos de propósito general.
[["$ {PIPESTATUS [@]}" = ~ [^ 0 \]]] && echo -e "Coincidencia - error encontrado" || echo -e "Sin coincidencia, todo bien" Esto probará todos los valores de la matriz a la vez y dará un mensaje de error si alguno de los valores de la tubería devueltos no son cero. Esta es una solución generalizada bastante robusta para detectar errores en una situación de canalización. –
http://unix.stackexchange.com/questions/14270/get-exit-status-of-process-thats-piped-to-another –