2010-03-09 8 views
35

Per reindirizzare (e append) stdout e stderr in un file, ma anche di visualizzarlo sul terminale, faccio questo:bash: redirect (e append) stdout e stderr su file e terminal e ottenere il corretto stato di uscita

command 2>&1 | tee -a file.txt 

Tuttavia, c'è un altro modo per farlo in modo tale da ottenere un valore preciso per lo stato di uscita?

Cioè, se provo $?, voglio vedere lo stato di uscita di command, non lo stato di uscita di tee.

So che posso utilizzare ${PIPESTATUS[0]} qui invece di $?, ma sto cercando un'altra soluzione che non implichi dover controllare lo PIPESTATUS.

+2

Perché non si desidera utilizzare 'PIPESTATUS'? –

+0

Duplicati: http://stackoverflow.com/questions/985876/tee-and-exit-status, http: // stackoverflow.com/domande/1221833/bash-tee-uscita-e-cattura-exit-status – jpalecek

risposta

30

Forse si potrebbe mettere il valore di uscita dal PIPESTATUS in $?

command 2>&1 | tee -a file.txt ; (exit ${PIPESTATUS}) 
6

Un'altra possibilità, con alcuni bash sapori, è quello di attivare l'opzione pipefail:

pipefail

Se impostato, il valore restituito di una pipeline è il valore dell'ultimo (più a destra) comm e per uscire con uno stato diverso da zero, oppure zero se tutti i comandi nella pipeline terminano correttamente. Questa opzione è disabilitata per impostazione predefinita.

set -o pipefail 
... 
command 2>&1 | tee -a file.txt || echo "Command (or tee?) failed with status $?" 

Ciò detto, l'unico modo per raggiungere PIPESTATUS funzionalità portabile (es quindi sarebbe anche funzionare con POSIX sh) è un po 'complicata, cioè richiede un file temporaneo propagare una stato di uscita del tubo di nuovo al processo shell genitore:

{ command 2>&1 ; echo $? >"/tmp/~pipestatus.$$" ; } | tee -a file.txt 
if [ "`cat \"/tmp/~pipestatus.$$\"`" -ne 0 ] ; then 
    ... 
fi 

o, incapsulando reimpiegate:

log2file() { 
    LOGFILE="$1" ; shift 
    { "[email protected]" 2>&1 ; echo $? >"/tmp/~pipestatus.$$" ; } | tee -a "$LOGFILE" 
    MYPIPESTATUS="`cat \"/tmp/~pipestatus.$$\"`" 
    rm -f "/tmp/~pipestatus.$$" 
    return $MYPIPESTATUS 
} 

log2file file.txt command param1 "param 2" || echo "Command failed with status $?" 

o, più genericamente forse:

save_pipe_status() { 
    STATUS_ID="$1" ; shift 
    "[email protected]" 
    echo $? >"/tmp/~pipestatus.$$.$STATUS_ID" 
} 

get_pipe_status() { 
    STATUS_ID="$1" ; shift 
    return `cat "/tmp/~pipestatus.$$.$STATUS_ID"` 
} 

save_pipe_status my_command_id ./command param1 "param 2" | tee -a file.txt 
get_pipe_status my_command_id || echo "Command failed with status $?" 

... 

rm -f "/tmp/~pipestatus.$$."* # do this in a trap handler, too, to be really clean 
3

C'è un modo POSIX arcano di fare questo:

exec 4>&1; R=$({ { command1; echo $? >&3 ; } | { command2 >&4; } } 3>&1); exec 4>&- 

Sarà impostare la variabile R al valore restituito command1 e tubo di uscita di command1 a command2, il cui output viene reindirizzato all'output della shell principale.

4

Usa processo di sostituzione:

command > >(tee -a "$logfile") 2>&1 

tee viene eseguito in una subshell così $? mantiene lo stato di uscita del comando .