Sé cómo usar tee
para escribir la salida ( STDOUT
) de aaa.sh
to bbb.out
, mientras lo sigo mostrando en el terminal:
./aaa.sh | tee bbb.out
¿Cómo escribiría ahora también STDERR
en un archivo llamado ccc.out
, sin dejar de mostrarlo?
Sé cómo usar tee
para escribir la salida ( STDOUT
) de aaa.sh
to bbb.out
, mientras lo sigo mostrando en el terminal:
./aaa.sh | tee bbb.out
¿Cómo escribiría ahora también STDERR
en un archivo llamado ccc.out
, sin dejar de mostrarlo?
Respuestas:
Supongo que todavía quieres ver STDERR y STDOUT en la terminal. Podrías buscar la respuesta de Josh Kelley, pero me parece que mantener un tail
fondo en el fondo que da como resultado tu archivo de registro es muy halagador y torpe. Observe cómo necesita mantener un exra FD y hacer la limpieza después de matarlo y técnicamente debería hacerlo en a trap '...' EXIT
.
Hay una mejor manera de hacer esto, y que ya ha descubierto que: 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? Sustitución de procesos y redirección de archivos:
command > >(tee -a stdout.log) 2> >(tee -a stderr.log >&2)
Vamos a dividirlo y explicarlo:
> >(..)
>(...)
(sustitución de proceso) crea un FIFO y lo tee
escuchamos. Luego, utiliza >
(redirección de archivos) para redirigir el STDOUT command
al FIFO en el que tee
está escuchando el primero .
Lo mismo para el segundo:
2> >(tee -a stderr.log >&2)
Usamos la sustitución de procesos nuevamente para hacer un tee
proceso que lea desde STDIN y lo descargue stderr.log
. tee
devuelve su entrada a STDOUT, pero como su entrada es nuestro STDERR, queremos redirigir tee
el STDOUT a nuestro STDERR nuevamente. Luego usamos la redirección de archivos para redirigir command
el STDERR de la entrada de FIFO (tee
STDIN de ).
Ver http://mywiki.wooledge.org/BashGuide/InputAndOutput
La sustitución de procesos es una de esas cosas realmente encantadoras que obtienes como un bono de elegir bash
como tu 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"
$ echo "HANG" > >(tee stdout.log) 2> >(tee stderr.log >&2)
que funciona, pero espera la entrada. ¿Hay una razón simple por la que esto sucede?
/bin/bash 2> err
y/bin/bash -i 2> err
(echo "Test Out";>&2 echo "Test Err") > >(tee stdout.log) 2> >(tee stderr.log >&2)
por qué no simplemente:
./aaa.sh 2>&1 | tee -a log
Esto simplemente redirige stderr
astdout
, por lo que tee hace eco tanto en el registro como en la pantalla. Tal vez me estoy perdiendo 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
./aaa.sh |& tee aaa.log
funciona (en bash)
set -o pipefail
seguido de ;
o &&
si no me equivoco.
Esto puede ser útil para las personas que lo encuentran a través de google. Simplemente descomente el ejemplo que desea probar. Por supuesto, no dude en 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}
exec >
significa, mover el objetivo de un descriptor de archivo a un determinado destino. El valor predeterminado es 1, por lo tanto, exec > /dev/null
mueve la salida de stdout a / dev / null de ahora en adelante en esta sesión. Los descriptores de archivo actuales para esta sesión se pueden ver haciendo ls -l /dev/fd/
. ¡Intentalo! Luego, vea qué sucede cuando emite. exec 2>/tmp/stderr.log.
Además, exec 3>&1
significa crear un nuevo descriptor de archivo con el número 3 y redirigirlo al objetivo del descriptor de archivo 1. En el ejemplo, el objetivo era la pantalla cuando se emitió el comando.
Para redirigir stderr a un archivo, muestre stdout a la pantalla y también guarde stdout en un archivo:
./aaa.sh 2> ccc.out | tee ./bbb.out
EDITAR : para mostrar stderr y 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, desea canalizar stdout 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 stdout en otro comando, pero puede solucionarlo haciendo malabares con los descriptores de archivo.
{ { ./aaa.sh | tee bbb.out; } 2>&1 1>&3 | tee ccc.out; } 3>&1 1>&2
Ver también ¿Cómo grep flujo 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 del proceso :
./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out)
Tenga en cuenta que en bash, este comando vuelve tan pronto como ./aaa.sh
termina, incluso si los tee
comandos aún se ejecutan (ksh y zsh esperan los subprocesos). Esto puede ser un problema si haces algo como eso ./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out); process_logs bbb.out ccc.out
. En ese caso, utilice el malabarismo del descriptor de archivo o ksh / zsh en su lugar.
sh
, útil para trabajos cron, donde la sustitución de procesos no está disponible.
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 (que no responde desde la parte superior de mi cabeza) va aquí: http://www.cygwin.com/ml/cygwin/2003-06/msg00772.html
En mi caso, un script ejecutaba un comando mientras redireccionaba stdout y stderr a un archivo, algo así como:
cmd > log 2>&1
Necesitaba actualizarlo de tal manera que cuando haya una falla, tome algunas acciones 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. Mientras que la respuesta aceptada de @lhunath se supone que debe hacer lo mismo, redirecciona stdout
y stderr
a diferentes archivos, que no es lo que quiero, pero me ayudó para llegar a la solución exacta que necesita:
(cmd 2> >(tee /dev/stderr)) > log
Con lo anterior, el registro tendrá una copia de ambos stdout
y stderr
puedo capturarlo stderr
desde mi script sin tener que preocuparme 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 lo 2>&1 1>&3
que en nuestro caso redirige el descriptor stderr
a stdout
y redirige stdout
al 3
. En este punto, stderr
y stdout
aún no se han combinado.
En efecto, el stderr
(as stdin
) se pasa a tee
donde se registra stderr.log
y también redirige al descriptor 3.
Y el descriptor lo 3
está registrando combined.log
todo el tiempo. Entonces el combined.log
contiene ambos stdout
y stderr
.
Si está usando zsh , puede usar múltiples redirecciones, por lo que ni siquiera necesita tee
:
./cmd 1>&1 2>&2 1>out_file 2>err_file
Aquí simplemente está redirigiendo cada transmisión a sí mismo y al archivo de destino.
Ejemplo completo
% (echo "out"; echo "err">/dev/stderr) 1>&1 2>&2 1>/tmp/out_file 2>/tmp/err_file
out
err
% cat /tmp/out_file
out
% cat /tmp/err_file
err
Tenga en cuenta que esto requiere MULTIOS
que se establezca la opción (que es la predeterminada).
MULTIOS
Realice so
tee
s implícitascat
cuando se intentan redireccionamientos múltiples (consulte Redirección ).