Forzar script de bash para usar tee sin tuberías desde la línea de comando

20

Tengo un script bash con muchos resultados y me gustaría cambiar ese script (no crear uno nuevo) para copiar todo el resultado en un archivo, al igual que sucedería si lo pasara por tee.

Tengo un archivo script.sh :

#!/bin/bash
init
do_something_that_outputs_stuff_to_STDOUT
launch_applications_that_output_to_STDOUT
fini

y me gustaría hacer una copia de STDOUT en un archivo script.log sin tener que escribir ./script.sh | tee script.logtodo el tiempo.

Gracias Tom

Tom
fuente
Lamento no haber dicho esto antes, pero el script es bastante largo, por eso estoy buscando una solución simple. Anexar | tee para cada línea es demasiado.
Tom

Respuestas:

15

No pude hacer funcionar la línea muy simple de Dennis, así que aquí hay un método mucho más complicado. Yo probaría el primero.

Como se mencionó, puede usar exec para redirigir el error estándar y la salida estándar para todo el script. Así:
exec > $LOGFILE 2>&1 esto generará todos los stderr y stdout a $ LOGFILE.

Ahora, dado que desea que esto se muestre en la consola, así como en un archivo de registro, también tendrá que usar una tubería con nombre para que el ejecutivo escriba y comience a leer.
(Técnicamente, One-liner de Dennis también hace esto, aunque obviamente de una manera diferente) La tubería en sí está creada con mkfifo $PIPEFILE. Luego haz lo siguiente.

# Comience a escribir tee en un archivo de registro, pero extraiga su entrada de nuestra tubería con nombre.
tee $ LOGFILE <$ PIPEFILE &

# captura el ID de proceso de tee para el comando de espera.
TEEPID = $!

# redirige el resto de stderr y stdout a nuestra canalización con nombre.
exec> $ PIPEFILE 2> & 1

echo "Haz tus comandos aquí"
echo "Todo su estándar saldrá teed".
echo "Así será su error estándar"> & 2

# cierra los descriptores de archivo stderr y stdout.
exec 1> & - 2> & -

# Espere a que termine el tee desde ahora que el otro extremo de la tubería se ha cerrado.
esperar $ TEEPID

Si desea ser exhaustivo, puede crear y destruir el archivo de canalización con nombre al principio y al final de su secuencia de comandos.

Para el registro, obtuve la mayor parte de esto de una publicación de blog muy informativa de un tipo aleatorio: ( Versión archivada )

Christopher Karel
fuente
Esto no parece funcionar en Cygwin. :-(
docwhat
Esta publicación hace un buen trabajo de educación. Muchas gracias.
Felipe Alvarez
27

Simplemente agregue esto al comienzo de su script:

exec > >(tee -ia script.log)

Eso agregará toda la salida enviada a stdout al archivo script.log, dejando los contenidos anteriores en su lugar. Si desea comenzar de nuevo cada vez que se ejecuta el script, simplemente agregue rm script.logjusto antes de ese execcomando o elimine -ael teecomando.

La -iopción hace teeque ignore las señales de interrupción y puede permitir teecapturar un conjunto de salida más completo.

Agregue esta línea para capturar también todos los errores (en un archivo separado):

exec 2> >(tee -ia scripterr.out)

Los espacios entre los >símbolos múltiples son importantes.

Pausado hasta nuevo aviso.
fuente
Solución muy interesante.
phaphink
2
Esta es una forma realmente elegante de hacerlo, en comparación con toda la basura que publiqué. Pero cuando creo un script con esas dos líneas, se cuelga y nunca sale correctamente.
Christopher Karel
¿Qué versión de Bash?
Pausado hasta nuevo aviso.
GNU bash, versión 3.2.25 (1) como parte de una instalación RHEL5.3. Parece que esta es la última versión de los repositorios oficiales.
Christopher Karel
Acabo de probarlo bajo cygwin en Bash 3.2.49 (23) -release y obtuve errores de descriptor de archivo. Sin embargo, trabajo en Bash 4.0.33 (1) -lanzamiento en Ubuntu 9.10, por lo que puede ser una cosa de Bash 4.
Pausado hasta nuevo aviso.
6

Esta es una versión combinada de la respuesta publicada por Dennis Williamson anteriormente. Agrega err y std a script.log en el orden correcto.

Agregue esta línea al comienzo de su script:

exec > >(tee -a script.log) 2>&1

Tenga en cuenta el espacio entre los >signos. Funciona para mí en GNU bash, versión 3.2.25 (1) -release (x86_64-redhat-linux-gnu)

ttt
fuente
5

Supongo que otro enfoque es algo como esto:

#!/bin/bash

# start grouping at the top
{ 
    # your script goes here
    do_something_that_outputs_stuff_to_STDOUT
    launch_applications_that_output_to_STDOUT

# and at the bottom, redirect everything from the grouped commands
} 2>&1 | tee script.log 
Glenn Jackman
fuente
3

Si desea una copia de la salida, puede usarla de teeesta manera:

  #!/bin/bash
  init
  do_something_that_outputs_stuff_to_STDOUT | tee script.log
  launch_applications_that_output_to_STDOUT | tee -a script.log
  fini

Sin embargo, esto solo registrará stdout en script.log. Si desea asegurarse de que tanto stderr como stdout se redirigen, use:

  #!/bin/bash
  init
  do_something_that_outputs_stuff_to_STDOUT 2>&1 | tee script.log
  launch_applications_that_output_to_STDOUT 2>&1 | tee -a script.log
  fini

Incluso podría hacerlo un poco más agradable con una pequeña función:

  #!/bin/bash

  LOGFILE="script.log"
  # Wipe LOGFILE if you don't want to add to it at each run
  rm -f "$LOGFILE"

  function logcmd() {
        local cmd="$1" logfile=${LOGFILE:-script.log} # Use default value if unset earlier

        # Launch command, redirect stderr to stdout and append to $logfile
        eval '$cmd' 2>&1 | tee -a "$logfile"
  }

  init
  logcmd "do_something_that_outputs_stuff_to_STDOUT"
  logcmd "launch_applications_that_output_to_STDOUT"
  fini
Phaphink
fuente
Buena respuesta. Si realmente solo se preocupa por el stdout, simplemente lo dejaría 2>&1afuera y lo conectaría al tee.
DaveParillo
Es cierto, solo incluí stderr porque la respuesta anterior de Igor se encargó de redirigirlo y pensé que era una buena idea.
phaphink
1

Simplemente usarías esto:

script logfile.txt
your script or command here

Esto genera todo en ese registro, todo, además de mostrarlo en la pantalla. Si desea la salida COMPLETA, así es cómo hacerlo.

Y luego puedes volver a reproducir el script si eres flojo.

Phishy
fuente
Puede ser bueno tener en cuenta que scriptes un paquete, por ejemplo. sudo apt-get install scriptpara instalarlo en distribuciones con sabor a Debian, adicionalmente script -ac 'command opts' ~/Documents/some_log.scriptpuede revisarse dentro de una terminal a través de algo como strings ~/Documents/some_log.script | more; stringsser una manera simple de desinfectar previamente algunas de las cosas que se script inyectan para permitir métodos más sofisticados de reproducción / visualización. De lo contrario, una respuesta sólida @Phishy, ​​porque scripttambién conserva las cosas interactivas también.
S0AndS0
0
#!/bin/bash
init
do_something_that_outputs_stuff_to_STDOUT > script.log 2>&1
launch_applications_that_output_to_STDOUT >> script.log 2>&1
fini

Puede ver más sobre cómo funciona aquí: http://www.xaprb.com/blog/2006/06/06/what-does-devnull-21-mean/

Igor Zinov'yev
fuente
1
Agregué un segundo> a la segunda línea en la respuesta de Igor para que no borre el registro escrito en la primera línea.
Doug Harris el
1
Sin embargo, quiere una copia, por lo que debe usarla tee.
phaphink
@Raphink: Así es :)
Tom