¿Cómo escribo stderr en un archivo mientras uso “tee” con una tubería?

Sé cómo usar tee para escribir la salida ( STDOUT ) de aaa.sh a bbb.out , mientras todavía se muestra en la terminal:

 ./aaa.sh | tee bbb.out 

¿Cómo ahora también STDERR escribir STDERR en un archivo llamado ccc.out , sin dejar de mostrarlo?

Supongo que aún quiere ver STDERR y STDOUT en la terminal. Podrías ir por la respuesta de Josh Kelley, pero me parece que debes mantener la tail en el fondo, lo que hace que tu archivo de registro sea muy hackoso y poco frecuente. Observe cómo debe mantener un exra FD y hacer una limpieza posterior matándolo y técnicamente debería hacerlo en una trap '...' EXIT .

Hay una mejor manera de hacerlo, y ya lo has descubierto: tee .

Solo que, en lugar de solo usarlo para su stdout, tenga una camiseta para stdout y otra para stderr. ¿Cómo lograrás esto? Sustitución de procesos y redirección de archivos:

 command > >(tee -a stdout.log) 2> >(tee -a stderr.log >&2) 

Vamos a dividirlo y explicar:

 > >(..) 

>(...) (sustitución de proceso) crea un FIFO y le permite escucharlo. Luego, usa > (redirección de archivos) para redirigir el STDOUT del command al FIFO que escucha su primer tee .

Lo mismo para el segundo:

 2> >(tee -a stderr.log >&2) 

Usamos la sustitución de procesos nuevamente para hacer un proceso en tee que lee desde STDIN y lo vuelca en stderr.log . tee devuelve su entrada en STDOUT, pero dado que su entrada es nuestro STDERR, queremos redireccionar STDOUT de tee a nuestro STDERR nuevamente. Luego utilizamos la redirección de archivos para redirigir el STDERR del comando a la entrada de la FIFO (STDIN de la tee ).

Ver http://mywiki.wooledge.org/BashGuide/InputAndOutput

La sustitución de procesos es una de esas cosas realmente encantadoras que obtienes como una ventaja de elegir bash como tu caparazón en lugar de sh (POSIX o Bourne).


En sh , tendrías que hacer las cosas manualmente:

 out="${TMPDIR:-/tmp}/out.$$" err="${TMPDIR:-/tmp}/err.$$" mkfifo "$out" "$err" trap 'rm "$out" "$err"' EXIT tee -a stdout.log < "$out" & tee -a stderr.log < "$err" >&2 & command >"$out" 2>"$err" 

¿por qué no simplemente?

 ./aaa.sh 2>&1 | tee -a log 

Esto simplemente redirige stderr a stdout , por lo que tee hace eco tanto para registrar como para pantalla. Tal vez me falta algo, porque algunas de las otras soluciones parecen realmente complicadas.

Nota: Desde la versión 4 de bash, puede usar |& como abreviatura para 2>&1 | :

 ./aaa.sh |& tee -a log 

Esto puede ser útil para las personas que encuentran esto a través de google. Simplemente elimine el comentario del ejemplo que desea probar. Por supuesto, puede cambiar el nombre de los archivos de salida.

 #!/bin/bash STATUSFILE=x.out LOGFILE=x.log ### All output to screen ### Do nothing, this is the default ### All Output to one file, nothing to the screen #exec > ${LOGFILE} 2>&1 ### All output to one file and all output to the screen #exec > >(tee ${LOGFILE}) 2>&1 ### All output to one file, STDOUT to the screen #exec > >(tee -a ${LOGFILE}) 2> >(tee -a ${LOGFILE} >/dev/null) ### All output to one file, STDERR to the screen ### Note you need both of these lines for this to work #exec 3>&1 #exec > >(tee -a ${LOGFILE} >/dev/null) 2> >(tee -a ${LOGFILE} >&3) ### STDOUT to STATUSFILE, stderr to LOGFILE, nothing to the screen #exec > ${STATUSFILE} 2>${LOGFILE} ### STDOUT to STATUSFILE, stderr to LOGFILE and all output to the screen #exec > >(tee ${STATUSFILE}) 2> >(tee ${LOGFILE} >&2) ### STDOUT to STATUSFILE and screen, STDERR to LOGFILE #exec > >(tee ${STATUSFILE}) 2>${LOGFILE} ### STDOUT to STATUSFILE, STDERR to LOGFILE and screen #exec > ${STATUSFILE} 2> >(tee ${LOGFILE} >&2) echo "This is a test" ls -l sdgshgswogswghthb_this_file_will_not_exist_so_we_get_output_to_stderr_aronkjegralhfaff ls -l ${0} 

Para redirigir stderr a un archivo, muestre stdout en pantalla y también guarde stdout en un archivo:

  ./aaa.sh 2> ccc.out |  tee ./bbb.out 

EDITAR : Para mostrar tanto stderr como stdout en la pantalla y también guardar ambos en un archivo, puede usar la redirección de E / S de bash:

 #!/bin/bash # Create a new file descriptor 4, pointed at the file # which will receive stderr. exec 4<>ccc.out # Also print the contents of this file to screen. tail -f ccc.out & # Run the command; tee stdout as normal, and send stderr # to our file descriptor 4. ./aaa.sh 2>&4 | tee bbb.out # Clean up: Close file descriptor 4 and kill tail -f. exec 4>&- kill %1 

En otras palabras, quiere tee bbb.out stdout a un filtro ( tee bbb.out ) y stderr a otro filtro ( tee ccc.out ). No hay una forma estándar de canalizar nada más que stdout en otro comando, pero puede solucionarlo haciendo malabares con los descriptores de archivos.

 { { ./aaa.sh | tee bbb.out; } 2>&1 1>&3 | tee ccc.out; } 3>&1 1>&2 

Consulte también Cómo grep stream de error estándar (stderr)? y ¿ Cuándo usarías un descriptor de archivo adicional?

En bash (y ksh y zsh), pero no en otros shells POSIX como el guión, puede usar la sustitución de procesos :

 ./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out) 

Tenga en cuenta que en bash, este comando regresa tan pronto como finaliza ./aaa.sh , incluso si los comandos de tee todavía se ejecutan (ksh y zsh esperan los subprocesos). Esto puede ser un problema si haces algo como ./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out); process_logs bbb.out ccc.out ./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out); process_logs bbb.out ccc.out . En ese caso, utilice el descriptor de archivo malabares o ksh / zsh en su lugar.

Si usa bash:

 # Redirect standard out and standard error separately % cmd >stdout-redirect 2>stderr-redirect # Redirect standard error and out together % cmd >stdout-redirect 2>&1 # Merge standard error with standard out and pipe % cmd 2>&1 |cmd2 

El crédito (no responde desde lo alto de mi cabeza) va aquí: http://www.cygwin.com/ml/cygwin/2003-06/msg00772.html

En mi caso, una secuencia de comandos ejecutaba un comando mientras redirigía tanto stdout como stderr a un archivo, algo así como:

 cmd > log 2>&1 

Necesitaba actualizarlo de modo que cuando haya un error, tome algunas medidas basadas en los mensajes de error. Por supuesto, podría eliminar el dup 2>&1 y capturar el stderr del script, pero luego los mensajes de error no entrarán en el archivo de registro como referencia. Si bien se supone que la respuesta aceptada de @lhunath hace lo mismo, redirige stdout y stderr a diferentes archivos, que no es lo que quiero, pero me ayudó a encontrar la solución exacta que necesito:

 (cmd 2> >(tee /dev/stderr)) > log 

Con lo anterior, log tendrá una copia de stdout y stderr y puedo capturar stderr de mi script sin tener que preocuparme por la stdout .

Lo siguiente funcionará para KornShell (ksh) donde la sustitución del proceso no está disponible,

 # create a combined(stdin and stdout) collector exec 3 <> combined.log # stream stderr instead of stdout to tee, while draining all stdout to the collector ./aaa.sh 2>&1 1>&3 | tee -a stderr.log 1>&3 # cleanup collector exec 3>&- 

El verdadero truco aquí es la secuencia de 2>&1 1>&3 que en nuestro caso redirige el stderr a stdout y redirige el stdout al descriptor 3 . En este punto, stderr y stdout aún no están combinados.

En efecto, stderr (como stdin ) se pasa a tee donde se registra en stderr.log y también redirige al descriptor 3.

Y el descriptor 3 está registrando en combined.log todo el tiempo. Entonces, combined.log contiene tanto stdout como stderr .