2013-12-13 18 views
5

Ho riscontrato un problema nel verificare se un determinato comando in una catena di comando multi-pipe ha generato un errore. Di solito questo non è difficile da controllare, ma né set -o pipefail né il controllo ${PIPESTATUS[@]} funziona nel mio caso. La configurazione è la seguente:Bash: verifica dello stato di uscita della catena di comando multi-pipe

cmd="$snmpcmd $snmpargs $agent $oid | grep <grepoptions> for_stuff | cut -d',' f$fields | sed 's/ubstitute/some_other_stuff/g'" 

Nota 1: il comando è stato testato a fondo e funziona perfettamente.

Ora, voglio memorizzare l'output di tale comando in un array chiamato procdata. Così, ho fatto:

declare -a procdata 
procdata=($(eval $cmd)) 

Nota-2: eval è necessario perché altrimenti $snmpcmd tiri su con un errore invalid option -- <grepoption> che non ha senso, perché <grepoption> non è un'opzione $snmpcmd ovviamente. A questo punto considero questo un bug con $snmpcmd ma questo è un altro spettacolo ...

Se si verifica un errore, procdata sarà vuoto. Tuttavia, potrebbe essere vuoto per due diversi motivi: o perché si è verificato un errore durante l'esecuzione dello $snmpcmd (ad es. Timeout) o perché grep non è riuscito a trovare ciò che stava cercando. Il problema è che devo essere in grado di distinguere tra questi due casi e gestirli separatamente.

Pertanto, set -o pipefail non è un'opzione in quanto propagherà qualsiasi errore e non è possibile distinguere quale parte della pipe ha avuto esito negativo. D'altra parte echo ${PIPESTATUS[@]} è sempre 0 dopo il procdata=($(eval $cmd)) anche se ho molti tubi!?. Tuttavia, se eseguo l'intero comando direttamente al prompt e richiama immediatamente echo ${PIPESTATUS[@]}, restituisce lo stato di uscita di tutte le pipe correttamente.

So che potrei associare il flusso err a stdout ma dovrei usare metodi euristici per verificare se gli elementi in procdata sono validi o messaggi di errore e corro il rischio di ottenere falsi positivi. Potrei anche eseguire il pipe stdout su /dev/null e catturare solo il flusso di errori e controllare se ${#procdata[@]} -eq 0. Ma dovrei ripetere la chiamata per ottenere i dati effettivi e l'intero comando è costoso in termini di tempo (circa 3-5s). Non vorrei chiamarlo due volte. O potrei usare un file temporaneo per scrivere errori, ma preferirei farlo senza il sovraccarico di creare/cancellare file.

Qualche idea su come posso farlo funzionare in bash?

Grazie

PS:

$ echo $BASH_VERSION 
4.2.37(1)-release 

risposta

3

Un certo numero di cose qui:

(1) Quando si dice eval $cmd e si tenta di ottenere i valori di uscita dei processi in cantiere contenuta nel il comando $cmd, echo "${PIPESTATUS[@]}" conterrà solo lo stato di uscita per eval. Invece di eval, devi fornire la riga di comando completa.

(2) È necessario ottenere lo PIPESTATUS mentre si assegna l'uscita della pipeline alla variabile. Tentare di farlo più tardi non avrebbe funzionato.


A titolo di esempio, si può dire:

foo=$(command | grep something | command2; echo "${PIPESTATUS[@]})" 

Questa cattura l'uscita della pipeline e la PIPESTATUS matrice nella variabile foo.

Si potrebbe ottenere l'output del comando in un array dicendo:

result=($(head -n -1 <<< "$foo")) 

e la PIPESTATUS array dicendo

tail -1 <<< "$foo" 
+0

ho avuto lo stesso pensiero. Sfortunatamente non funziona, ma non sono sicuro del perché. Inserisco i comandi esatti perché potrei non vedere l'ovvio: – user3040975

+0

'local cmdargs =" - CHf, -m $ mibs -v $ snmpver -c $ community $ agent "; local procdatacmd = "$ tblcmd $ cmdargs $ proc_table"; procdatacmd + = "| cut -d ',' -f $ fields | grep -we -e | sort | uniq -c | sed 's/^ * \ | \" // g; s//,/g' ; echo $ {PIPESTATUS [@]} "' Allora faccio: 'declare -a procdata = ($ (testa -n -1 <<< $ procdatacmd)). L'output è vuoto ... solo nulla e è attivo e funzionante sull'agente. Cosa ... – user3040975

+0

@ user3040975 Sembra che non ci sia _running_ qualsiasi comando nella riga sopra. – devnull