Copia stdout e stderr in un file di registro e lasciali sulla console all'interno dello script stesso


11

Usando bash, come faccio a copiare stderr e stdout in un file di registro e anche lasciarli visualizzati sulla console?

Vorrei farlo all'interno dello script stesso usando un exec.

Ci ho provato

exec &>> log.out

echo "This is stdout"
echo "This is stderr" >&2

Ma quanto sopra non stampa nulla sulla console. Come posso ottenere questo in bash?


C'è una risposta molto votata a una domanda simile su StackOverflow che risponde a questa domanda in modo abbastanza completo. stackoverflow.com/a/692407/208257
Dan Burton,

Risposte:


10

Stai cercando tee.

Vedi man teeper i dettagli.

Per combinarlo con exec, è necessario utilizzare la sostituzione del processo . (Vedi man bashper i dettagli.)

exec &> >(tee  log.out)
echo "This is stdout"
echo "This is stderr" >&2

L'ho visto. Fare exec 2>&1 | tee -a log.outsolo stampe sulla console, niente nel file di registro. Forse non sto usando la sintassi giusta?
Adarshr,

@adarshr Per favore ricontrolla il tuo codice. Ho scritto teein combinazione con la sostituzione del processo .
H.-Dirk Schmitt,

1
Ciò unisce stdout e stderr e può avere lo stesso tipo di effetto collaterale menzionato nella mia risposta.
Stéphane Chazelas,

@StephaneChazelas Guarda il codice di esempio nella domanda: quello era l'intento dell'OP.
H.-Dirk Schmitt,

Quello che intendo per unione è che ora gli errori dello script vanno su stdout. Quindi, se qualcuno lo fa, the-script | wc -lad esempio, conterà anche le righe di errore e non vedrai gli errori.
Stéphane Chazelas,

5

Tu puoi fare:

: > log # empty log file if necessary
{ { {

  ...the script

} 3>&- | tee -a log >&3 3>&-
exit "${PIPESTATUS[0]}"
} 2>&1 | tee -a log >&2 3>&-
} 3>&1
exit "${PIPESTATUS[0]}"

Puoi anche scriverlo come:

: > log # empty log file if necessary
exec 2> >(tee -a log >&2) > >(tee -a log)

...the script

Ma poiché bash non sta aspettando l'avvio di quei processi >(...), questo ha il brutto effetto di inviare a volte qualcosa al terminale dopo che il comando è tornato, il che può avere effetti anche più cattivi (come scartare silenziosamente quell'output) se l'attributo "tostop" del terminale è acceso.

In ogni caso, creando stdoutuna pipe in entrambe le soluzioni e poiché due comandi generano in modo indipendente i messaggi di output e di errore, ciò influirà sul buffering di output e sull'ordine in cui vengono visualizzati i messaggi di output e di errore.


Penso che al tuo secondo tee manchi un reindirizzamento a stderr. Dovrebbe essere:tee -a log >&2 3>&-
richvdh

5

So che questo è un vecchio post, ma perché non farlo?

echo "hi" >> log.txt #stdout -> log
echo "hi" | tee -a log.txt #stdout -> log & stdout
echo "hi" &>> log.txt #stdout & stderr -> log
echo "hi" |& tee -a log.txt #stdout & stderr -> log & stdout

E, naturalmente, se vuoi stdout puoi semplicemente stampare regolarmente.

Puoi farlo con qualsiasi combinazione di flussi che desideri, semplicemente usando quei due comandi di base.

So di essere venuto qui e non ho avuto una risposta facile da capire / attuare, spero che questo possa essere d'aiuto a qualcun altro che sta lottando.

A proposito, per i rumori là fuori come il mio sé precedente, tutto il teecomando fa è inviare l'input stdin sia allo stdout che al file specificato come argomento successivo. -asta per append, quindi non sovrascrivere il file con ogni uso del comando. Se hai ulteriori domande, trovo che questa sia una risorsa molto utile per imparare rapidamente bash.


1
Grazie per aver contribuito. D'altra parte, ho completamente dimenticato cosa stavo cercando di fare :-)
adarshr

1
+1 per dare esempi multipli, simpatici e semplici.
Tim

2

Un altro modo per farlo è utilizzare i reindirizzamenti all'interno delle funzioni.

#!/bin/bash

function1 () {
    echo 'STDOUT from function 1'
    echo 'STDERR from function 1' >&2
}

function2 () {
    echo 'STDOUT from function 2'
    echo 'STDERR from function 2' >&2
}


function3 () {
    echo 'STDOUT from function 3'
    echo 'STDERR from function 3' >&2
}

main() {
    function1
    function2
    function3
}

main 2>&1 |tee log.txt

Qui abbiamo una mainfunzione che invoca tutte le altre funzioni. Ora il reindirizzamento STDOUTe STDERRdi mainfunzione tee.


IMHO questo è il modo più pulito per farlo, almeno per casi semplici. Grazie.
ACK_stoverflow,
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.