So usare teeper scrivere l'output ( STDOUT) di aaa.sha bbb.out, mentre lo visualizzo ancora nel terminale:
./aaa.sh | tee bbb.out
Come potrei ora scrivere anche STDERRsu un file chiamato ccc.out, pur avendo ancora visualizzato?
So usare teeper scrivere l'output ( STDOUT) di aaa.sha bbb.out, mentre lo visualizzo ancora nel terminale:
./aaa.sh | tee bbb.out
Come potrei ora scrivere anche STDERRsu 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 tailsfondo 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 teeascoltarlo. Quindi, utilizza >(reindirizzamento dei file) per reindirizzare lo STDOUT commandal FIFO su cui il primo teesta ascoltando.
Stessa cosa per il secondo:
2> >(tee -a stderr.log >&2)
Usiamo nuovamente la sostituzione di processo per creare un teeprocesso che legge da STDIN e lo scarica stderr.log. teerestituisce il suo input su STDOUT, ma poiché il suo input è il nostro STDERR, vogliamo reindirizzare nuovamente lo teeSTDOUT al nostro STDERR. Quindi usiamo il reindirizzamento dei file per reindirizzare lo commandSTDERR sull'input della FIFO ( teeSTDIN).
Vedi http://mywiki.wooledge.org/BashGuide/InputAndOutput
La sostituzione del processo è una di quelle cose davvero adorabili che si ottiene come bonus di scegliere bashcome 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> erre/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 stderra 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.logfunziona (in bash).
set -o pipefailseguita 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/nullsposta 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>&1significa 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.shtermina, anche se i teecomandi 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>&1e 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 stdoute stderrsu 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 stdoute stderrposso catturare stderrdal 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>&3quale nel nostro caso reindirizza il stderrto stdoute reindirizza il stdoutto al descrittore 3. A questo punto i stderre stdoutnon sono ancora combinati.
In effetti, il simbolo stderr(as stdin) viene passato a teedove accede stderr.loge reindirizza anche al descrittore 3.
E il descrittore 3lo registra combined.logcontinuamente. Quindi combined.logcontiene entrambi stdoute 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' MULTIOSopzione da impostare (che è l'impostazione predefinita).
MULTIOSEseguire
tees ocats impliciti quando si tentano reindirizzamenti multipli (consultare Reindirizzamento ).