So usare tee
per scrivere l'output ( STDOUT
) di aaa.sh
a bbb.out
, mentre lo visualizzo ancora nel terminale:
./aaa.sh | tee bbb.out
Come potrei ora scrivere anche STDERR
su un file chiamato ccc.out
, pur avendo ancora visualizzato?
So usare tee
per scrivere l'output ( STDOUT
) di aaa.sh
a bbb.out
, mentre lo visualizzo ancora nel terminale:
./aaa.sh | tee bbb.out
Come potrei ora scrivere anche STDERR
su un file chiamato ccc.out
, pur avendo ancora visualizzato?
Risposte:
Presumo che tu voglia vedere ancora STDERR e STDOUT sul terminale. Potresti cercare la risposta di Josh Kelley, ma trovo che mantenere uno tail
sfondo in giro che produca il tuo file di registro sia molto complicato e ingombrante. Si noti come è necessario mantenere un FD exra e procedere successivamente alla pulizia uccidendolo e tecnicamente dovrebbe farlo in untrap '...' EXIT
.
C'è un modo migliore per farlo e l'hai già scoperto: tee
.
Solo, invece di usarlo solo per il tuo stdout, hai un tee per stdout e uno per stderr. Come lo farai? Sostituzione del processo e reindirizzamento dei file:
command > >(tee -a stdout.log) 2> >(tee -a stderr.log >&2)
Dividiamolo e spieghiamo:
> >(..)
>(...)
(sostituzione di processo) crea un FIFO e consente di tee
ascoltarlo. Quindi, utilizza >
(reindirizzamento dei file) per reindirizzare lo STDOUT command
al FIFO su cui il primo tee
sta ascoltando.
Stessa cosa per il secondo:
2> >(tee -a stderr.log >&2)
Usiamo nuovamente la sostituzione di processo per creare un tee
processo che legge da STDIN e lo scarica stderr.log
. tee
restituisce il suo input su STDOUT, ma poiché il suo input è il nostro STDERR, vogliamo reindirizzare nuovamente lo tee
STDOUT al nostro STDERR. Quindi usiamo il reindirizzamento dei file per reindirizzare lo command
STDERR sull'input della FIFO ( tee
STDIN).
Vedi http://mywiki.wooledge.org/BashGuide/InputAndOutput
La sostituzione del processo è una di quelle cose davvero adorabili che si ottiene come bonus di scegliere bash
come shell invece di sh
(POSIX o Bourne).
In sh
, dovresti fare le cose manualmente:
out="${TMPDIR:-/tmp}/out.$$" err="${TMPDIR:-/tmp}/err.$$"
mkfifo "$out" "$err"
trap 'rm "$out" "$err"' EXIT
tee -a stdout.log < "$out" &
tee -a stderr.log < "$err" >&2 &
command >"$out" 2>"$err"
$ echo "HANG" > >(tee stdout.log) 2> >(tee stderr.log >&2)
che funziona, ma attende input. C'è un semplice motivo per cui questo accade?
/bin/bash 2> err
e/bin/bash -i 2> err
(echo "Test Out";>&2 echo "Test Err") > >(tee stdout.log) 2> >(tee stderr.log >&2)
perché non semplicemente:
./aaa.sh 2>&1 | tee -a log
Questo reindirizza semplicemente stderr
a stdout
, quindi tee fa eco sia al registro che allo schermo. Forse mi manca qualcosa, perché alcune delle altre soluzioni sembrano davvero complicate.
Nota: dalla versione 4 di bash è possibile utilizzare |&
come abbreviazione di 2>&1 |
:
./aaa.sh |& tee -a log
./aaa.sh |& tee aaa.log
funziona (in bash).
set -o pipefail
seguita da ;
o &&
se non sbaglio.
Questo può essere utile per chi lo trova tramite google. Basta decommentare l'esempio che si desidera provare. Naturalmente, sentiti libero di rinominare i file di output.
#!/bin/bash
STATUSFILE=x.out
LOGFILE=x.log
### All output to screen
### Do nothing, this is the default
### All Output to one file, nothing to the screen
#exec > ${LOGFILE} 2>&1
### All output to one file and all output to the screen
#exec > >(tee ${LOGFILE}) 2>&1
### All output to one file, STDOUT to the screen
#exec > >(tee -a ${LOGFILE}) 2> >(tee -a ${LOGFILE} >/dev/null)
### All output to one file, STDERR to the screen
### Note you need both of these lines for this to work
#exec 3>&1
#exec > >(tee -a ${LOGFILE} >/dev/null) 2> >(tee -a ${LOGFILE} >&3)
### STDOUT to STATUSFILE, stderr to LOGFILE, nothing to the screen
#exec > ${STATUSFILE} 2>${LOGFILE}
### STDOUT to STATUSFILE, stderr to LOGFILE and all output to the screen
#exec > >(tee ${STATUSFILE}) 2> >(tee ${LOGFILE} >&2)
### STDOUT to STATUSFILE and screen, STDERR to LOGFILE
#exec > >(tee ${STATUSFILE}) 2>${LOGFILE}
### STDOUT to STATUSFILE, STDERR to LOGFILE and screen
#exec > ${STATUSFILE} 2> >(tee ${LOGFILE} >&2)
echo "This is a test"
ls -l sdgshgswogswghthb_this_file_will_not_exist_so_we_get_output_to_stderr_aronkjegralhfaff
ls -l ${0}
exec >
significa, spostare la destinazione di un descrittore di file su una determinata destinazione. Il valore predefinito è 1, quindi exec > /dev/null
sposta l'output di stdout su / dev / null d'ora in poi in questa sessione. I descrittori di file correnti per questa sessione possono essere visualizzati facendo ls -l /dev/fd/
. Provalo! Quindi vedere cosa succede quando si emette exec 2>/tmp/stderr.log.
Inoltre, exec 3>&1
significa creare un nuovo descrittore di file con il numero 3 e reindirizzarlo alla destinazione del descrittore di file 1. Nell'esempio, la destinazione era la schermata quando è stato emesso il comando.
Per reindirizzare stderr su un file, visualizzare stdout sullo schermo e anche salvare stdout su un file:
./aaa.sh 2> ccc.out | tee ./bbb.out
EDIT : per visualizzare sia stderr che stdout sullo schermo e anche salvarli entrambi in un file, puoi usare il reindirizzamento I / O di bash :
#!/bin/bash
# Create a new file descriptor 4, pointed at the file
# which will receive stderr.
exec 4<>ccc.out
# Also print the contents of this file to screen.
tail -f ccc.out &
# Run the command; tee stdout as normal, and send stderr
# to our file descriptor 4.
./aaa.sh 2>&4 | tee bbb.out
# Clean up: Close file descriptor 4 and kill tail -f.
exec 4>&-
kill %1
In altre parole, vuoi reindirizzare stdout in un filtro ( tee bbb.out
) e stderr in un altro filtro ( tee ccc.out
). Non esiste un modo standard per reindirizzare qualsiasi cosa diversa da stdout in un altro comando, ma puoi aggirare questo manipolando i descrittori di file.
{ { ./aaa.sh | tee bbb.out; } 2>&1 1>&3 | tee ccc.out; } 3>&1 1>&2
Vedi anche Come grep flusso di errori standard (stderr)? e quando useresti un descrittore di file aggiuntivo?
In bash (e ksh e zsh), ma non in altre shell POSIX come dash, è possibile utilizzare la sostituzione del processo :
./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out)
Attenzione che in bash, questo comando ritorna non appena ./aaa.sh
termina, anche se i tee
comandi sono ancora eseguiti (ksh e zsh attendono i sottoprocessi). Questo potrebbe essere un problema se fai qualcosa del genere ./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out); process_logs bbb.out ccc.out
. In tal caso, utilizzare invece il descrittore di file giocoleria o ksh / zsh.
sh
, utile per i lavori cron, in cui la sostituzione del processo non è disponibile.
Se si utilizza bash:
# Redirect standard out and standard error separately
% cmd >stdout-redirect 2>stderr-redirect
# Redirect standard error and out together
% cmd >stdout-redirect 2>&1
# Merge standard error with standard out and pipe
% cmd 2>&1 |cmd2
Il credito (non rispondendo dalla cima della mia testa) va qui: http://www.cygwin.com/ml/cygwin/2003-06/msg00772.html
Nel mio caso, uno script eseguiva il comando mentre reindirizzava sia stdout che stderr su un file, qualcosa del tipo:
cmd > log 2>&1
Avevo bisogno di aggiornarlo in modo tale che in caso di errore, eseguire alcune azioni in base ai messaggi di errore. Ovviamente potrei rimuovere il dup 2>&1
e catturare lo stderr dallo script, ma poi i messaggi di errore non andranno nel file di registro come riferimento. Mentre la risposta accettata da @lhunath dovrebbe fare lo stesso, reindirizza stdout
e stderr
su file diversi, che non è quello che voglio, ma mi ha aiutato a trovare la soluzione esatta di cui ho bisogno:
(cmd 2> >(tee /dev/stderr)) > log
Con quanto sopra, il registro avrà una copia di entrambi stdout
e stderr
posso catturare stderr
dal mio script senza doversi preoccupare stdout
.
Di seguito funzionerà per KornShell (ksh) in cui la sostituzione del processo non è disponibile,
# create a combined(stdin and stdout) collector
exec 3 <> combined.log
# stream stderr instead of stdout to tee, while draining all stdout to the collector
./aaa.sh 2>&1 1>&3 | tee -a stderr.log 1>&3
# cleanup collector
exec 3>&-
Il vero trucco qui, è la sequenza della 2>&1 1>&3
quale nel nostro caso reindirizza il stderr
to stdout
e reindirizza il stdout
to al descrittore 3
. A questo punto i stderr
e stdout
non sono ancora combinati.
In effetti, il simbolo stderr
(as stdin
) viene passato a tee
dove accede stderr.log
e reindirizza anche al descrittore 3.
E il descrittore 3
lo registra combined.log
continuamente. Quindi combined.log
contiene entrambi stdout
e stderr
.
Se stai usando zsh , puoi usare più reindirizzamenti, quindi non hai nemmeno bisogno di tee
:
./cmd 1>&1 2>&2 1>out_file 2>err_file
Qui stai semplicemente reindirizzando ogni flusso a se stesso e al file di destinazione.
Esempio completo
% (echo "out"; echo "err">/dev/stderr) 1>&1 2>&2 1>/tmp/out_file 2>/tmp/err_file
out
err
% cat /tmp/out_file
out
% cat /tmp/err_file
err
Si noti che questo richiede l' MULTIOS
opzione da impostare (che è l'impostazione predefinita).
MULTIOS
Eseguire
tee
s ocat
s impliciti quando si tentano reindirizzamenti multipli (consultare Reindirizzamento ).