bash - redireccionamiento - ¿Cómo puedo obtener STDOUT y STDERR para ir al terminal y un archivo de registro?
stderr linux (7)
Tengo un script que se ejecutará de forma interactiva por usuarios no técnicos. El script escribe actualizaciones de estado en STDOUT para que el usuario pueda estar seguro de que el script se está ejecutando correctamente.
Quiero que tanto STDOUT como STDERR se redirijan al terminal (para que el usuario pueda ver que el script está funcionando y ver si hubo un problema). También quiero que se redirijan ambas secuencias a un archivo de registro.
He visto un montón de soluciones en la red. Algunos no funcionan y otros son terriblemente complicados. He desarrollado una solución viable (que ingresaré como respuesta), pero es kludgy.
La solución perfecta sería una sola línea de código que podría incorporarse al comienzo de cualquier secuencia de comandos que envíe ambas secuencias tanto al terminal como a un archivo de registro.
EDITAR: redirige STDERR a STDOUT y canaliza el resultado a tee works, pero depende de que los usuarios recuerden redirigir y canalizar la salida. Quiero que el registro sea a prueba de errores y automático (por lo que me gustaría poder insertar la solución en el script en sí).
Aproximadamente media década después ...
Creo que esta es la "solución perfecta" buscada por el OP.
Aquí hay un trazador de líneas que puede agregar a la parte superior de su secuencia de comandos Bash:
exec > >(tee -a $HOME/logfile) 2>&1
Aquí hay un pequeño script que demuestra su uso:
#!/usr/bin/env bash
exec > >(tee -a $HOME/logfile) 2>&1
# Test redirection of STDOUT
echo test_stdout
# Test redirection of STDERR
ls test_stderr___this_file_does_not_exist
(Nota: Esto solo funciona con Bash. No funcionará con / bin / sh)
Adaptado de here ; el original no, por lo que puedo decir, captura STDERR en el archivo de registro. Reparado con una nota de here .
Creé un script llamado "RunScript.sh". El contenido de este script es:
${APP_HOME}/${1}.sh ${2} ${3} ${4} ${5} ${6} 2>&1 | tee -a ${APP_HOME}/${1}.log
Lo llamo así:
./RunScript.sh ScriptToRun Param1 Param2 Param3 ...
Esto funciona, pero requiere que los scripts de la aplicación se ejecuten a través de un script externo. Es un poco kludgy.
Un año después, aquí hay un viejo script bash para registrar cualquier cosa. Por ejemplo,
teelog make ...
logs a un nombre de registro generado (y vea el truco para registrar makes anidados también).
#!/bin/bash
me=teelog
Version="2008-10-9 oct denis-bz"
Help() {
cat <<!
$me anycommand args ...
logs the output of "anycommand ..." as well as displaying it on the screen,
by running
anycommand args ... 2>&1 | tee `day`-command-args.log
That is, stdout and stderr go to both the screen, and to a log file.
(The Unix "tee" command is named after "T" pipe fittings, 1 in -> 2 out;
see http://en.wikipedia.org/wiki/Tee_(command) ).
The default log file name is made up from "command" and all the "args":
$me cmd -opt dir/file logs to `day`-cmd--opt-file.log .
To log to xx.log instead, either export log=xx.log or
$me log=xx.log cmd ...
If "logdir" is set, logs are put in that directory, which must exist.
An old xx.log is moved to /tmp//$USER-xx.log .
The log file has a header like
# from: command args ...
# run: date pwd etc.
to show what was run; see "From" in this file.
Called as "Log" (ln -s $me Log), Log anycommand ... logs to a file:
command args ... > `day`-command-args.log
and tees stderr to both the log file and the terminal -- bash only.
Some commands that prompt for input from the console, such as a password,
don''t prompt if they "| tee"; you can only type ahead, carefully.
To log all "make" s, including nested ones like
cd dir1; /$(MAKE)
cd dir2; /$(MAKE)
...
export MAKE="$me make"
!
# See also: output logging in screen(1).
exit 1
}
#-------------------------------------------------------------------------------
# bzutil.sh denisbz may2008 --
day() { # 30mar, 3mar
/bin/date +%e%h | tr ''[A-Z]'' ''[a-z]'' | tr -d '' ''
}
edate() { # 19 May 2008 15:56
echo `/bin/date "+%e %h %Y %H:%M"`
}
From() { # header # from: $* # run: date pwd ...
case `uname` in Darwin )
mac=" mac `sw_vers -productVersion`"
esac
cut -c -200 <<!
${comment-#} from: $@
${comment-#} run: `edate` in $PWD `uname -n` $mac `arch`
!
# mac $PWD is pwd -L not -P real
}
# log name: day-args*.log, change this if you like --
logfilename() {
log=`day`
[[ $1 == "sudo" ]] && shift
for arg
do
log="$log-${arg##*/}" # basename
(( ${#log} >= 100 )) && break # max len 100
done
# no blanks etc in logfilename please, tr them to "-"
echo $logdir/` echo "$log".log | tr -C ''.:+=[:alnum:]_/n'' - `
}
#-------------------------------------------------------------------------------
case "$1" in
-v* | --v* )
echo "$0 version: $Version"
exit 1 ;;
"" | -* )
Help
esac
# scan log= etc --
while [[ $1 == [a-zA-Z_]*=* ]]; do
export "$1"
shift
done
: ${logdir=.}
[[ -w $logdir ]] || {
echo >&2 "error: $me: can''t write in logdir $logdir"
exit 1
}
: ${log=` logfilename "$@" `}
[[ -f $log ]] &&
/bin/mv "$log" "/tmp/$USER-${log##*/}"
case ${0##*/} in # basename
log | Log ) # both to log, stderr to caller''s stderr too --
{
From "$@"
"$@"
} > $log 2> >(tee /dev/stderr) # bash only
# see http://wooledge.org:8000/BashFAQ 47, stderr to a pipe
;;
* )
#-------------------------------------------------------------------------------
{
From "$@" # header: from ... date pwd etc.
"$@" 2>&1 # run the cmd with stderr and stdout both to the log
} | tee $log
# mac tee buffers stdout ?
esac
Use "T" para redirigir a un archivo y la pantalla. Dependiendo del shell que use, primero tiene que redirigir stderr a stdout usando
./a.out 2>&1 | tee output
o
./a.out |& tee output
En csh, hay un comando incorporado llamado "script" que capturará todo lo que va a la pantalla a un archivo. Usted comienza escribiendo "script", luego haciendo lo que sea que quiera capturar, luego pulse control-D para cerrar el archivo de script. No sé de un equivalente para sh / bash / ksh.
Además, dado que ahora ha indicado que estos son sus propios scripts sh que puede modificar, puede hacer la redirección internamente rodeando todo el script con llaves o corchetes, como
#!/bin/sh
{
... whatever you had in your script before
} 2>&1 | tee output.file
Use el programa tee y dup stderr para stdout.
program 2>&1 | tee > logfile
Utilice el comando de script
en su script ( script
comando man 1)
Cree un shellscript contenedor (2 líneas) que configure el script () y luego las llamadas salgan.
Parte 1: wrap.sh
#!/bin/sh
script -c ''./realscript.sh''
exit
Parte 2: realscript.sh
#!/bin/sh
echo ''Output''
Resultado:
~: sh wrap.sh
Script started, file is typescript
Output
Script done, file is typescript
~: cat typescript
Script started on fr. 12. des. 2008 kl. 18.07 +0100
Output
Script done on fr. 12. des. 2008 kl. 18.07 +0100
~:
para redirigir stderr a stdout añádalo a su orden: 2>&1
Para enviar a la terminal e iniciar sesión en el archivo debe usar tee
Ambos juntos se verían así:
mycommand 2>&1 | tee mylogfile.log
EDITAR: para incrustar en tu script, harías lo mismo. Entonces tu guion
#!/bin/sh
whatever1
whatever2
...
whatever3
terminaría como
#!/bin/sh
( whatever1
whatever2
...
whatever3 ) 2>&1 | tee mylogfile.log