2013-07-21 19 views
5

in bash, se eseguo un paio di comandi collegati insieme all'interno di apici inversi, come posso scoprire lo stato di uscita del primo comando?bash pipestatus nel comando backticked?

In questo caso, sto cercando di ottenere il "1". che posso ottenere tramite [0] PIPESTATUS se non sto usando apici inversi, ma che non sembra funzionare quando voglio salvare l'output:

## PIPESTATUS[0] works to give me the exit status of 'false': 
$ false | true; 
$ echo $? ${PIPESTATUS[0]} ${PIPESTATUS[1]}; 
0 1 0 

## doesn't work: 
$ a=`false | true`; 
$ echo $? ${PIPESTATUS[0]} ${PIPESTATUS[1]}; 
0 0 

Più in generale, sto cercando di realizzare: salvare il ultima riga della produzione di qualche programma a una variabile, ma in grado di dire se il programma non è riuscita:

$ myvar=` ./someprogram | tail -1 `; 
$ if [ "what do i put here" ]; then echo "program failed!"; fi 

Idealmente mi piacerebbe anche capire che cosa sta succedendo, non solo quale sia la risposta.

Grazie.

risposta

4

Provare a impostare l'opzione pipefail. Restituisce l'ultimo comando della pipeline che ha avuto esito negativo. Un esempio:

Per prima cosa ho disattivarlo:

set +o pipefail 

creare uno script perl (script.pl) per testare la pipeline:

#!/usr/bin/env perl 

use warnings; 
use strict; 

if (@ARGV) { 
    die "Line1\nLine2\nLine3\n"; 
} 
else { 
    print "Line1\nLine2\nLine3\n"; 
} 

Run nella riga di comando:

myvar=`perl script.pl | tail -1` 
echo $? "$myvar" 

Che produce:

0 Line3 

Sembra corretto, lascia vedere con pipefail abilitato:

set -o pipefail 

ed eseguire il comando:

myvar=`perl script.pl fail 2>&1 | tail -1` 
echo $? "$myvar" 

che i rendimenti:

255 Line3 
+0

Grazie, funziona. Non sapevo del "pipefail". – jerry

+0

Funziona anche in sh. Grazie. – AnkurTank

2

La mia soluzione stava usando FIFO e il bash "coproc" incorporato per ottenere i messaggi e lo stato da ciascuna com mand nel tubo. Non avevo mai usato i fifo prima. (oh ragazzo, la prossima volta sto usando BashEclipse su Fedora). Si è trasformato in un meccanismo generalizzato per la gestione di qualsiasi comando di pipe. Ho risolto il problema, ma non in 10 o 20 righe di codice. più come 200 per una soluzione riutilizzabile drop-in robusta (mi ci sono voluti tre giorni per farlo).

condivido i miei appunti:

* stderr for all pipe commands goes to the fifos. 
    If you want to get messages from stdout, you must redirect '1>&2', like this: 
    PIPE_ARRAY=("cat ${IMG}.md5" "cut -f1 -d\" \" 1>&2") 
    You must put "2>/fifo" first. Otherwise it won\'t work. example: 
    cat ${IMG}.md5 | cut -f1 -d' ' 1>&2 
    becomes: 
    cat ${IMG}.md5 2>/tmp/fifo_s0 | cut -f1 -d" " 2>/tmp/fifo_s1 1>&2 ; PSA=("${PIPESTATUS[@]}") 

* With more tha one fifo, I found that you must read each fifo in turn. 
    When "fifo1" gets written to, "fifo0" reads are blocked until you read "fifo1" 
    I did\'nt use any special tricks like "sleep", "cat", or extra file descriptors 
    to keep the fifos open. 

* PIPESTATUS[@] must be copied to an array immediately after the pipe command returns. 
    _Any_ reads of PIPESTATUS[@] will erase the contents. Super volatile ! 
    "manage_pipe()" appends '; PSA=("${PIPESTATUS[@]}")' to the pipe command string 
    for this reason. "$?" is the same as the last element of "${PIPESTATUS[@]}", 
    and reading it seems to destroy "${PIPESTATUS[@]}", but it's not absolutly verifed. 

run_pipe_cmd() { 
    declare -a PIPE_ARRAY MSGS 
    PIPE_ARRAY=("dd if=${gDEVICE} bs=512 count=63" "md5sum -b >${gBASENAME}.md5") 
    manage_pipe PIPE_ARRAY[@] "MSGS" # (pass MSGS name, not the array) 
} 
manage_pipe() { 
    # input - $1 pipe cmds array, $2 msg retvar name 
    # output - fifo msg retvar 
    # create fifos, fifo name array, build cnd string from $1 (re-order redirection if needed) 
    # run coprocess 'coproc execute_pipe FIFO[@] "$CMDSTR"' 
    # call 'read_fifos FIFO[@] "M" "S"' (pass names, not arrays for $2 and $3) 
    # calc last_error, call _error, _errorf 
    # set msg retvar values (eval ${2}[${i}]='"${Msg[${i}]}"') 
} 
read_fifos() { 
    # input - $1 fifo array, $2 msg retvar name, $3 status retvar name 
    # output - msg, status retvars 
    # init local fifo_name, pipe_cmd_status, msg arrays 
    # do read loop until all 'quit' msgs are received 
    # set msg, status retvar values (i.e. eval ${3}[${i}]='"${Status[${i}]}"' 
} 
execute_pipe() { 
    # $1 fifo array, $2 cmdstr, $3 msg retvar, $4 status retvar 
    # init local fifo_name, pipe_cmd_status arrays 
    # execute command string, get pipestaus (eval "$_CMDSTR" 1>&2) 
    # set fifo statuses from copy of PIPESTATUS 
    # write 'status', 'quit' msgs to fifo 
} 
0
## PIPESTATUS[0] works to give me the exit status of 'false': 
$ false | true 
$ echo $? ${PIPESTATUS[0]} ${PIPESTATUS[1]} 
0 1 0 

## Populate a $PIPESTATUS2 array: 
$ a=`false | true; printf :%s "${PIPESTATUS[*]}"` 
$ ANS=$?; PIPESTATUS2=(${a##*:}) 
$ [ -n "${a%:*}" ] && a="${a%:*}" && a="${a%$'\n'}" || a="" 
$ echo $ANS ${PIPESTATUS2[0]} ${PIPESTATUS2[1]}; 
0 1 0 

Ciò consente di risparmiare $PIPESTATUS serie di sub-shell a fine $a e poi estratti utilizzando una variabile di shell substring removal; vedere l'esempio più lungo e la descrizione che ho dato a Get exit codes of a pipe when output is assigned to variable (Command Substitution).