Para aclarar, ¿quieres que stderr vaya tanto a la pantalla como al archivo?
Charles Duffy
Lo hice, editaré mi publicación para aclarar eso. Creo que la solución de lhunath será suficiente. ¡Gracias por toda la ayuda!
jparanich
Respuestas:
786
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 tailfondo 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 teeescuchamos. Luego, utiliza >(redirección de archivos) para redirigir el STDOUT commandal FIFO en el que teeestá escuchando el primero .
Lo mismo para el segundo:
2>>(tee -a stderr.log >&2)
Usamos la sustitución de procesos nuevamente para hacer un teeproceso que lea desde STDIN y lo descargue stderr.log. teedevuelve su entrada a STDOUT, pero como su entrada es nuestro STDERR, queremos redirigir teeel STDOUT a nuestro STDERR nuevamente. Luego usamos la redirección de archivos para redirigir commandel STDERR de la entrada de FIFO (tee STDIN de ).
La sustitución de procesos es una de esas cosas realmente encantadoras que obtienes como un bono de elegir bashcomo 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"
Intenté esto: $ 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?
Justin
1
@SillyFreak No entiendo lo que quieres hacer o cuál es el problema que tienes. prueba de eco; exit no produce ningún resultado en stdout, por lo que err permanecerá vacío.
lhunath
1
gracias por ese comentario; Luego descubrí cuál era mi error lógico: cuando se invoca como un shell interactivo, bash imprime un símbolo del sistema y hace eco de la salida a stderr. Sin embargo, si se redirige stderr, bash comienza como no interactivo de manera predeterminada; comparar /bin/bash 2> erry/bin/bash -i 2> err
Silly Freak
15
Y para aquellos que "ver para creer", una prueba rápida:(echo "Test Out";>&2 echo "Test Err") > >(tee stdout.log) 2> >(tee stderr.log >&2)
Matthew Wilcoxson
2
Wow . Buena respuesta: "Vamos a dividirlo y explicarlo" +1
jalanb
674
por qué no simplemente:
./aaa.sh 2>&1| tee -a log
Esto simplemente redirige stderrastdout , 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 |:
Eso funciona bien si desea que stdout (canal 1) y stderr (canal 2) se registren en el mismo archivo (un solo archivo que contiene la mezcla de stdout y sterr). La otra solución más complicada le permite separar stdout y stderr en 2 archivos diferentes (stdout.log y stderr.log, respectivamente). A veces eso es importante, a veces no lo es.
Tyler Rick
19
Las otras soluciones son mucho más complicadas de lo necesario en muchos casos. Este funciona perfectamente para mí.
dkamins
13
El problema con este método es que pierde el código de salida / estado del proceso aaa.sh, que puede ser importante (por ejemplo, cuando se usa en un archivo MAKE). No tiene este problema con la respuesta aceptada.
Stefaan
99
si no te importa fusionar stdout / stderr, entonces ./aaa.sh |& tee aaa.logfunciona (en bash)
jfs
55
@ Stefaan Creo que puede conservar el estado de salida si antepone la cadena de comandos set -o pipefailseguido de ;o &&si no me equivoco.
David
59
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}
No, y supongo que el ejecutivo puede usar algunas explicaciones. exec >significa, mover el objetivo de un descriptor de archivo a un determinado destino. El valor predeterminado es 1, por lo tanto, exec > /dev/nullmueve 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>&1significa 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.
drumfire
¡El ejemplo para mostrar stdout y stderr tanto en pantalla como en archivos separados es increíble! ¡Muchas gracias!
Marcello de Sales
21
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
Espero que el usuario quiera que stderr vaya a su consola además del archivo, aunque no se especificó explícitamente.
Charles Duffy el
2
Debería haber sido más claro, también quería stderr en la pantalla. Todavía disfruté la solución de Josh Kelley, pero encuentro la de lhunath para satisfacer más mis necesidades. ¡Gracias chicos!
jparanich
18
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>&11>&3| tee ccc.out;}3>&11>&2
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.shtermina, incluso si los teecomandos 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.
Esta parece ser la única respuesta que permite mantener las secuencias stdout / stderr tal cual (por ejemplo, no fusionarlas). ¡Dulce!
user1338062
El enfoque más simple para sh, útil para trabajos cron, donde la sustitución de procesos no está disponible.
Roger Dueck
13
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
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>&1y 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 stdouty stderra 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 stdouty stderrpuedo capturarlo stderrdesde 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>&11>&3| tee -a stderr.log 1>&3# cleanup collector
exec 3>&-
El verdadero truco aquí, es la secuencia de lo 2>&1 1>&3que en nuestro caso redirige el descriptor stderra stdouty redirige stdoutal 3. En este punto, stderry stdoutaún no se han combinado.
En efecto, el stderr(as stdin) se pasa a teedonde se registra stderr.logy también redirige al descriptor 3.
Y el descriptor lo 3está registrando combined.logtodo el tiempo. Entonces el combined.logcontiene ambos stdouty stderr.
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 atrap '...' 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:
Vamos a dividirlo y explicarlo:
>(...)
(sustitución de proceso) crea un FIFO y lotee
escuchamos. Luego, utiliza>
(redirección de archivos) para redirigir el STDOUTcommand
al FIFO en el quetee
está escuchando el primero .Lo mismo para el segundo:
Usamos la sustitución de procesos nuevamente para hacer un
tee
proceso que lea desde STDIN y lo descarguestderr.log
.tee
devuelve su entrada a STDOUT, pero como su entrada es nuestro STDERR, queremos redirigirtee
el STDOUT a nuestro STDERR nuevamente. Luego usamos la redirección de archivos para redirigircommand
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 desh
(POSIX o Bourne).En
sh
, tendrías que hacer las cosas manualmente:fuente
$ 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:
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 para2>&1 |
:fuente
./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.
fuente
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 haciendols -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:
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 :
fuente
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.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 :
Tenga en cuenta que en bash, este comando vuelve tan pronto como
./aaa.sh
termina, incluso si lostee
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.fuente
sh
, útil para trabajos cron, donde la sustitución de procesos no está disponible.Si usa bash:
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
fuente
En mi caso, un script ejecutaba un comando mientras redireccionaba stdout y stderr a un archivo, algo así como:
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, redireccionastdout
ystderr
a diferentes archivos, que no es lo que quiero, pero me ayudó para llegar a la solución exacta que necesita:Con lo anterior, el registro tendrá una copia de ambos
stdout
ystderr
puedo capturarlostderr
desde mi script sin tener que preocuparmestdout
.fuente
Lo siguiente funcionará para KornShell (ksh) donde la sustitución del proceso no está disponible,
El verdadero truco aquí, es la secuencia de lo
2>&1 1>&3
que en nuestro caso redirige el descriptorstderr
astdout
y redirigestdout
al3
. En este punto,stderr
ystdout
aún no se han combinado.En efecto, el
stderr
(asstdin
) se pasa atee
donde se registrastderr.log
y también redirige al descriptor 3.Y el descriptor lo
3
está registrandocombined.log
todo el tiempo. Entonces elcombined.log
contiene ambosstdout
ystderr
.fuente
Si está usando zsh , puede usar múltiples redirecciones, por lo que ni siquiera necesita
tee
:Aquí simplemente está redirigiendo cada transmisión a sí mismo y al archivo de destino.
Ejemplo completo
Tenga en cuenta que esto requiere
MULTIOS
que se establezca la opción (que es la predeterminada).fuente