2008-12-12 17 views
57

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.¿Cómo consigo STDOUT y STDERR para ir al terminal y un archivo de registro?

Deseo que tanto STDOUT como STDERR se redirijan al terminal (para que el usuario pueda ver que la secuencia de comandos funciona 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 única 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: Redirigir STDERR a STDOUT y canalizar 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 eso me gustaría poder insertar la solución en el script).

+0

Para otros lectores: pregunta similar: http://stackoverflow.com/questions/692000/how-do-i-write-stderr-to-a-file-while-using-tee-with-a-pipe – pevik

Respuesta

92

Use "T" para redirigir a un archivo y a la pantalla. Dependiendo de la cáscara se utiliza, primero hay que redirigir stderr a stdout utilizando

./a.out 2>&1 | tee output 

o

./a.out |& tee output 

En CSH, hay un comando integrado 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.

También, ya que ahora han indicado que estos son sus propios guiones sh que se puede modificar, se puede hacer el cambio de dirección internamente que rodea todo el guión con los apoyos o soportes, como

#!/bin/sh 
    { 
    ... whatever you had in your script before 
    } 2>&1 | tee output.file 
+3

No sabía que podrías poner los comandos en los guiones del intérprete de comandos. Interesante. – Jamie

+1

¡También aprecio el atajo de Corchete! Por alguna razón, '2> & 1 | tee -a filename' no estaba guardando stderr en el archivo de mi script, ¡pero funcionó bien cuando copié el comando y lo pegué en la terminal! El truco de corchete funciona bien, sin embargo. –

+3

Tenga en cuenta que la distinción entre stdout y stderr se perderá, ya que tee imprime todo en stdout. – Flimm

1

Usar el tee programa y dup stderr a stdout.

program 2>&1 | tee > logfile 
2

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 

me llaman así:

./RunScript.sh ScriptToRun Param1 Param2 Param3 ... 

Esto funciona, pero requiere secuencias de comandos de la aplicación que se ejecutan a través de un script externo. Es un poco kludgy.

+6

Perderá la agrupación de argumentos que contienen espacios en blanco con ** $ 1 $ 2 $ 3 ... **, debe usar (w/quotes): ** "$ @" ** – NVRAM

3

la de redirigir stderr a stdout adjuntar la presente en su comando: 2>&1 para dar salida a la terminal e iniciar sesión en el archivo se debe utilizar tee

Los dos juntos se vería así:

mycommand 2>&1 | tee mylogfile.log 

EDIT: Para incrustar en tu script, harías lo mismo.Así que su script

#!/bin/sh 
whatever1 
whatever2 
... 
whatever3 

terminaría como

#!/bin/sh 
(whatever1 
whatever2 
... 
whatever3) 2>&1 | tee mylogfile.log 
+0

Tenga en cuenta que la distinción entre stdout y stderr será perdido, como tee imprime todo para stdout. – Flimm

0

Utilice el comando script en el script (hombre 1 guión)

Crear una shellscript envoltorio (2 líneas) que pone en marcha la escritura () y luego llama a la salida.

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 
~: 
1

Un año más tarde, aquí está una vieja escritura del golpe para el registro cualquier cosa. Por ejemplo,
teelog make ... registros a un nombre de registro generado (y ver el truco para el registro anidado make s 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: [email protected] 
${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 "[email protected]" `} 
[[ -f $log ]] && 
    /bin/mv "$log" "/tmp/$USER-${log##*/}" 


case ${0##*/} in # basename 
log | Log) # both to log, stderr to caller's stderr too -- 
{ 
    From "[email protected]" 
    "[email protected]" 
} > $log 2> >(tee /dev/stderr) # bash only 
    # see http://wooledge.org:8000/BashFAQ 47, stderr to a pipe 
;; 

*) 
#------------------------------------------------------------------------------- 
{ 
    From "[email protected]" # header: from ... date pwd etc. 

    "[email protected]" 2>&1 # run the cmd with stderr and stdout both to the log 

} | tee $log 
    # mac tee buffers stdout ? 

esac 
+0

Sé que esto es muy tarde para agregar un comentario, pero solo tenía que agradecerle por este guión. ¡Muy útil y bien documentado! – stephenmm

+0

Gracias @stephenmm; es * nunca * demasiado tarde para decir "útil" o "podría mejorarse". – denis

10

cerca de la mitad de una década después ...

creo que esto es el " solución perfecta "buscada por el OP.

Aquí hay un chiste que puede agregar a la parte superior de la 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 sólo funciona con Bash. Será no trabajo con/bin/sh.)

Adaptado de here; el original no, por lo que puedo decir, captura STDERR en el archivo de registro. Se corrigió con una nota del here.

+0

Tenga en cuenta que la distinción entre stdout y stderr se perderá, ya que tee imprime todo para stdout. – Flimm

+0

@Flimm stderr podría ser redirigido a un proceso de tee diferente que de nuevo podría ser redirigido a stderr. – jarno

Cuestiones relacionadas