Redireccionar todos los comandos posteriores 'stderr usando exec

43

Tengo un archivo bash que necesito para redirigir toda la salida a un archivo, el registro de depuración y el terminal. Necesito redirigir stdout y stderr a la depuración y registrarlo para todos los comandos en el script.

No quiero agregar 2>&1 | tee -a $DEBUGpara cada comando en el archivo. Yo podría vivir con | tee -a $DEBUG.

Recuerdo que había una manera de hacerlo con algo así exec 2>&1.

Actualmente estoy usando algo como lo siguiente:

#!/bin/bash
DEBUGLOG=/tmp/debug
exec 2>&1
somecommand | tee -a $DEBUGLOG
somecommand2 | tee -a $DEBUGLOG
somecommand3 | tee -a $DEBUGLOG

Pero no funciona. ¿Alguien tiene una solución / puede explicar la causa?

Avi
fuente
1
En algunos shells, |&funciona como un atajo para 2>&1 |, es al menos un poco más conveniente.
Kevin

Respuestas:

39

En cuanto a una solución para redirigir muchos comandos a la vez:

#!/bin/bash
{
    somecommand 
    somecommand2
    somecommand3
} 2>&1 | tee -a $DEBUGLOG

Por qué su solución original no funciona: exec 2> & 1 redirigirá la salida de error estándar a la salida estándar de su shell, que, si ejecuta su script desde la consola, será su consola. la redirección de la tubería en los comandos solo redirigirá la salida estándar del comando.

Desde el punto de vista de somecommand, su salida estándar entra en una tubería conectada teey el error estándar entra en el mismo archivo / pseudofile que el error estándar del shell, que redirige a la salida estándar del shell, que será el consola si ejecuta su programa desde la consola.

La única forma verdadera de explicarlo es ver lo que realmente sucede:

El entorno original de su shell podría verse así si lo ejecuta desde la terminal:

stdin -> /dev/pts/42
stdout -> /dev/pts/42
stderr -> /dev/pts/42

Después de redirigir el error estándar a la salida estándar ( exec 2>&1), usted ... básicamente no cambia nada. Pero si redirige la salida estándar del script a un archivo, terminaría con un entorno como este:

stdin -> /dev/pts/42
stdout -> /your/file
stderr -> /dev/pts/42

Luego, redirigir el error estándar del shell a la salida estándar terminaría así:

stdin -> /dev/pts/42
stdout -> /your/file
stderr -> /your/file

Ejecutar un comando heredará este entorno. Si ejecuta un comando y lo canaliza al tee, el entorno del comando sería:

stdin -> /dev/pts/42
stdout -> pipe:[4242]
stderr -> /your/file

Por lo tanto, el error estándar de su comando todavía entra en lo que el shell usa como error estándar.

En realidad, puede ver el entorno de un comando mirando /proc/[pid]/fd: use ls -ltambién para enumerar el contenido del enlace simbólico. El 0archivo aquí es entrada estándar, 1salida estándar y 2error estándar. Si el comando abre más archivos (y la mayoría de los programas lo hacen), también los verá. Un programa también puede elegir redirigir o cerrar su entrada / salida estándar y reutilizar 0, 1y 2.

BatchyX
fuente
41

Puede usar exec como este en la parte superior de su script:

exec > >(tee "$HOME/somefile.log") 2>&1

Por ejemplo:

#!/bin/bash -

exec > >(tee "$HOME/somefile.log") 2>&1

echo "$HOME"
echo hi
date
date +%F
echo bye 1>&2

Me da salida al archivo $HOME/somefile.logy al terminal de esta manera:

/home/saml
hi
Sun Jan 20 13:54:17 EST 2013
2013-01-20
bye
slm
fuente
2
Tenga en cuenta que esto utiliza bashisms: puede que no funcione en otros shells (por ejemplo, guión). Pero como la pregunta especificaba bash, +1.
Richard Hansen
8
@RichardHansen, la sustitución de procesos es una característica que introdujo ksh, no bash, y también es compatible con zsh, por lo que no lo llamaría bashism .
Stéphane Chazelas
66
@StephaneChazelas: Haces un buen punto. Solo quería señalar que la sintaxis no es compatible con el estándar POSIX y, por lo tanto, no funcionará universalmente en los /bin/shscripts (muchas personas usan erróneamente la sintaxis bash en los /bin/shscripts).
Richard Hansen el
Para mí esto da /dev/fd/62: Operation not supportedalguna pista?
Eun
1
¿Hay alguna manera de no redirigir stderr excepto al archivo de registro? Si el script original es myscripty ejecuto ./myscript > /dev/null, aún debería ver de byedónde proviene echo bye >&2.
Martin Jambon
0

Escriba stderr y stdout en un archivo, muestre stderr en la pantalla (en stdout)

exec 2> >(tee -a -i "$HOME/somefile.log")
exec >> "$HOME/somefile.log"

Útil para crons, por lo que puede recibir errores (y solo errores) por correo

Lluís
fuente