Reindirizza lo stderr di tutti i comandi successivi usando exec


43

Ho un file bash che devo reindirizzare tutto l'output su un file, registro di debug e sul terminale. Ho bisogno di reindirizzare sia stdout che stderr al debug e registrarlo per tutti i comandi nello script.

Non voglio aggiungere 2>&1 | tee -a $DEBUGper ogni singolo comando nel file. Potrei vivere con | tee -a $DEBUG.

Ricordo che c'era un modo per farlo con qualcosa del genere exec 2>&1.

Attualmente sto usando qualcosa di simile al seguente:

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

ma non funziona. Qualcuno ha una soluzione / può spiegare la causa?


1
In alcune shell, |&funziona come scorciatoia per 2>&1 |, è almeno leggermente più conveniente.
Kevin,

Risposte:


39

Per quanto riguarda una soluzione per reindirizzare molti comandi contemporaneamente:

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

Perché la soluzione originale non funziona: exec 2> & 1 reindirizzerà l'output di errore standard all'output standard della shell, che, se si esegue lo script dalla console, sarà la console. il reindirizzamento pipe sui comandi reindirizzerà solo l'output standard del comando.

Dal punto di vista di somecommand, il suo output standard va in una pipe collegata teee l'errore standard va nello stesso file / pseudofile dell'errore standard della shell, che reindirizzi all'output standard della shell, che sarà il console se si esegue il programma dalla console.

L'unico vero modo per spiegarlo è vedere cosa succede davvero:

L'ambiente originale della shell potrebbe apparire così se lo esegui dal terminale:

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

Dopo aver reindirizzato l'errore standard in output standard ( exec 2>&1), in pratica ... non si modifica nulla. Ma se reindirizzi l'output standard dello script su un file, finiresti con un ambiente come questo:

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

Quindi il reindirizzamento dell'errore standard della shell nell'output standard finirebbe così:

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

L'esecuzione di un comando erediterà questo ambiente. Se si esegue un comando e lo si reindirizza a T, l'ambiente del comando sarebbe:

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

Quindi l'errore standard del tuo comando va ancora in quello che la shell usa come suo errore standard.

Puoi effettivamente vedere l'ambiente di un comando guardando /proc/[pid]/fd: usa ls -lper elencare anche il contenuto del link simbolico. Il 0file qui è input standard, 1output standard ed 2errore standard. Se il comando apre più file (e la maggior parte dei programmi lo fanno), li vedrai anche. Un programma può anche scegliere di reindirizzare o chiudere il suo input / output standard e riutilizzarlo 0, 1e 2.


41

Puoi usare exec in questo modo nella parte superiore del tuo script:

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

Per esempio:

#!/bin/bash -

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

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

Mi dà l'output al file $HOME/somefile.loge al terminale in questo modo:

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

2
Si noti che questo utilizza basismi - potrebbe non funzionare in altre shell (ad esempio, trattino). Ma poiché la domanda specificava bash, +1.
Richard Hansen,

8
@RichardHansen, la sostituzione del processo è una funzionalità introdotta da ksh, non bash ed è supportata anche da zsh, quindi non la definirei un bashism .
Stéphane Chazelas,

6
@StephaneChazelas: fai un buon punto. Volevo solo sottolineare che la sintassi non è supportata dallo standard POSIX e quindi non funzionerà universalmente negli /bin/shscript (molte persone usano erroneamente la sintassi bash negli /bin/shscript).
Richard Hansen,

Per me questo dà /dev/fd/62: Operation not supportedqualche indizio?
Eun,

1
C'è un modo per non reindirizzare stderr se non al file di registro? Se lo script originale è myscripted eseguo ./myscript > /dev/null, dovrei ancora vedere da byedove proviene echo bye >&2.
Martin Jambon,

0

Scrivi stderr e stdout su un file, visualizza stderr sullo schermo (su stdout)

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

Utile per i croni, in modo da poter ricevere errori (e solo errori) per posta

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.