linux - comandos - bourne again shell
¿Cómo escribo stderr en un archivo mientras uso "tee" con una tubería? (8)
Sé cómo usar tee
para escribir la salida ( STDOUT
) de aaa.sh
a bbb.out
, mientras se muestra en el terminal:
./aaa.sh | tee bbb.out
¿Cómo ahora también escribiría STDERR
en un archivo llamado ccc.out
, mientras aún se muestra?
En mi caso, una secuencia de comandos estaba ejecutando un comando mientras redirigía stdout y stderr a un archivo, algo como:
cmd > log 2>&1
Necesitaba actualizarlo de tal manera que cuando se produce un error, tome algunas medidas basadas en los mensajes de error. Por supuesto, podría eliminar el dup 2>&1
y capturar el stderr de la secuencia de comandos, 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, el registro tendrá una copia de stdout
y stderr
y puedo capturar stderr
de mi script sin tener que preocuparme por stdout
.
En otras palabras, desea canalizar la tee bbb.out
en un filtro ( tee bbb.out
) y stderr en otro filtro ( tee ccc.out
). No hay una forma estándar de canalizar otra cosa que no sea 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 corriente 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 tee
aún 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, use el descriptor de archivos malabares o ksh / zsh en su lugar.
Esto puede ser útil para las personas que encuentran esto a través de google. Simplemente descomenta el ejemplo que quieres probar. Por supuesto, siéntase libre de 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}
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 stderr
a stdout
y redirige 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 el combined.log
contiene tanto stdout
como stderr
.
Para redireccionar stderr a un archivo, muestre stdout a pantalla y también guarde stdout a un archivo:
./aaa.sh 2>ccc.out | tee ./bbb.out
EDITAR : para mostrar tanto stderr como stdout a 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
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 (sin responder desde lo alto de mi cabeza) va aquí: http://www.cygwin.com/ml/cygwin/2003-06/msg00772.html
Supongo que todavía desea ver STDERR y STDOUT en el terminal. Podrías buscar la respuesta de Josh Kelley, pero me parece que mantener la tail
en el fondo, lo que hace que tu archivo de registro sea muy difícil y confuso. Observe cómo necesita mantener un FD exra 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 usarlo solo para su stdout, tenga una camiseta para stdout y una para stderr. ¿Cómo vas a lograr esto? Proceso de sustitución 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 escuchar en él. Luego, usa >
(redirección de archivos) para redirigir el command
STDOUT al FIFO en el que está escuchando 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 se lee desde STDIN y lo descarga en stderr.log
. tee
devuelve su entrada a STDOUT, pero como su entrada es nuestro STDERR, queremos redireccionar tee
STDOUT de te a STDERR nuevamente. Luego usamos la redirección de archivos para redirigir el STDERR del comando a la entrada de FIFO (STDIN de tee
).
Ver http://mywiki.wooledge.org/BashGuide/InputAndOutput
La sustitución de procesos es una de esas cosas realmente encantadoras que se obtienen como una ventaja de elegir bash
como su shell 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 el tee hace eco tanto en el registro como en la pantalla. Tal vez me esté perdiendo algo, porque algunas de las otras soluciones parecen realmente complicadas.
Nota: Desde la versión 4 de bash puede usar |&
como una abreviatura de 2>&1 |
:
./aaa.sh |& tee -a log