Sé cómo usar teepara escribir la salida ( STDOUT) de aaa.shto bbb.out, mientras lo sigo mostrando en el terminal:
./aaa.sh | tee bbb.out
¿Cómo escribiría ahora también STDERRen un archivo llamado ccc.out, sin dejar de mostrarlo?
Sé cómo usar teepara escribir la salida ( STDOUT) de aaa.shto bbb.out, mientras lo sigo mostrando en el terminal:
./aaa.sh | tee bbb.out
¿Cómo escribiría ahora también STDERRen 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 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 ).
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 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"
$ 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> erry/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 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 |:
./aaa.sh |& tee -a log
./aaa.sh |& tee aaa.logfunciona (en bash)
set -o pipefailseguido 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/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.
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.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.
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>&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>&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>&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.
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 MULTIOSque se establezca la opción (que es la predeterminada).
MULTIOSRealice so
tees implícitascatcuando se intentan redireccionamientos múltiples (consulte Redirección ).