Redireciona o stderr de todos os comandos subseqüentes usando exec

34

Eu tenho um arquivo bash que eu preciso para redirecionar toda a saída para um arquivo, log de depuração, bem como para o terminal. Eu preciso redirecionar stdout e stderr para a depuração e registrá-lo para todos os comandos no script.

Eu não quero adicionar 2>&1 | tee -a $DEBUG para cada comando no arquivo. Eu poderia viver com | tee -a $DEBUG .

Eu lembro que havia uma maneira de fazer isso com algo como exec 2>&1 .

Atualmente, estou usando algo como o seguinte:

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

mas não funciona. Alguém tem uma solução / pode explicar a causa?

    
por Avi 20.01.2013 / 19:01

2 respostas

36

Quanto a uma solução para redirecionar muitos comandos de uma só vez:

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

Por que sua solução original não funciona: exec 2 > & 1 redirecionará a saída de erro padrão para a saída padrão do seu shell, que, se você executar seu script a partir do console, será o seu console. o redirecionamento de pipe nos comandos somente redirecionará a saída padrão do comando.

Do ponto de vista de somecommand , sua saída padrão entra em um pipe conectado a tee e o erro padrão entra no mesmo arquivo / pseudofile que o erro padrão do shell, que você redireciona para o padrão saída do shell, que será o console se você executar seu programa a partir do console.

A única maneira verdadeira de explicar isso é ver o que realmente acontece:

O ambiente original do seu shell pode ser assim se você executá-lo a partir do terminal:

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

Depois de redirecionar o erro padrão para a saída padrão ( exec 2>&1 ), você ... basicamente não muda nada. Mas se você redirecionar a saída padrão do script para um arquivo, você acabará com um ambiente como este:

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

Em seguida, redirecionar o erro padrão do shell para a saída padrão terminaria assim:

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

A execução de um comando herdará esse ambiente. Se você executar um comando e canalizá-lo para tee, o ambiente do comando seria:

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

Portanto, o erro padrão do seu comando ainda vai para o que o shell usa como erro padrão.

Você pode realmente ver o ambiente de um comando procurando em /proc/[pid]/fd : use ls -l para também listar o conteúdo do link simbólico. O arquivo 0 aqui é a entrada padrão, 1 é a saída padrão e 2 é o erro padrão. Se o comando abrir mais arquivos (e a maioria dos programas), você também os verá. Um programa também pode optar por redirecionar ou fechar sua entrada / saída padrão e reutilizar 0 , 1 e 2 .

    
por 20.01.2013 / 19:16
34

Você pode usar exec assim no topo do seu script:

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

Por exemplo:

#!/bin/bash -

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

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

Me dá saída para o arquivo $HOME/somefile.log e para o terminal assim:

/home/saml
hi
Sun Jan 20 13:54:17 EST 2013
2013-01-20
bye
    
por 20.01.2013 / 19:57