bash stdout stderr tee

bash: redirige(y agrega) stdout y stderr al archivo y terminal y obtiene el estado de salida correcto



tee (4)

¿Quizás podría poner el valor de salida de PIPESTATUS en $?

command 2>&1 | tee -a file.txt ; ( exit ${PIPESTATUS} )

Para redirigir (y anexar) stdout y stderr a un archivo, y al mismo tiempo mostrarlo en la terminal, hago esto:

command 2>&1 | tee -a file.txt

Sin embargo, ¿hay alguna otra forma de hacer esto para que obtenga un valor preciso para el estado de salida?

Es decir, si pruebo $? , Quiero ver el estado de salida del command , no el estado de tee de tee .

Sé que puedo usar ${PIPESTATUS[0]} aquí en lugar de $? , pero estoy buscando otra solución que no implique tener que verificar PIPESTATUS .


Hay una forma arcana de POSIX de hacer esto:

exec 4>&1; R=$({ { command1; echo $? >&3 ; } | { command2 >&4; } } 3>&1); exec 4>&-

Establecerá la variable R en el valor de retorno de command1 , y la salida de pipe de command1 en command2 , cuyo resultado se redirige a la salida del shell primario.


Otra posibilidad, con algunos sabores bash , es activar la opción pipefail :

pipefail

Si se establece, el valor de retorno de una tubería es el valor del último comando (más a la derecha) para salir con un estado distinto de cero, o cero si todos los comandos en la tubería salen exitosamente. Esta opción está deshabilitada por defecto.

set -o pipefail ... command 2>&1 | tee -a file.txt || echo "Command (or tee?) failed with status $?"

Habiendo dicho esto, la única manera de lograr la funcionalidad de PIPESTATUS de forma portátil (por ejemplo, también podría funcionar con POSIX sh ) es un poco complicada, es decir, requiere un archivo temporal para propagar un estado de salida de tubería al proceso de shell primario:

{ command 2>&1 ; echo $? >"/tmp/~pipestatus.$$" ; } | tee -a file.txt if [ "`cat /"/tmp/~pipestatus.$$/"`" -ne 0 ] ; then ... fi

o, encapsulando para reutilizar:

log2file() { LOGFILE="$1" ; shift { "$@" 2>&1 ; echo $? >"/tmp/~pipestatus.$$" ; } | tee -a "$LOGFILE" MYPIPESTATUS="`cat /"/tmp/~pipestatus.$$/"`" rm -f "/tmp/~pipestatus.$$" return $MYPIPESTATUS } log2file file.txt command param1 "param 2" || echo "Command failed with status $?"

o, más genéricamente quizás:

save_pipe_status() { STATUS_ID="$1" ; shift "$@" echo $? >"/tmp/~pipestatus.$$.$STATUS_ID" } get_pipe_status() { STATUS_ID="$1" ; shift return `cat "/tmp/~pipestatus.$$.$STATUS_ID"` } save_pipe_status my_command_id ./command param1 "param 2" | tee -a file.txt get_pipe_status my_command_id || echo "Command failed with status $?" ... rm -f "/tmp/~pipestatus.$$."* # do this in a trap handler, too, to be really clean


Use la sustitución del proceso:

command > >( tee -a "$logfile" ) 2>&1

tee se ejecuta en una subshell así que $? mantiene el estado de salida del comando .