2013-07-21 25 views
5

w bashu, jeśli wykonam kilka poleceń połączonych ze sobą w backticks, w jaki sposób mogę sprawdzić status wyjścia pierwszego polecenia?bash pipestatus w poleceniu backticked?

tj. W tym przypadku próbuję uzyskać "1". który można uzyskać poprzez PIPESTATUS [0], jeśli nie używam odwrócone, pojedyncze apostrofy, ale które nie wydają się działać, gdy chcę zapisując wynik:

## 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 

Bardziej ogólnie staram się osiągnąć: uratować ostatni wiersz wyjścia jakiegoś programu do zmiennej, ale będzie w stanie powiedzieć, czy program nie powiodło się:

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

Idealnie chciałbym również, aby zrozumieć, co się dzieje, nie tylko to, co jest odpowiedzią.

Dzięki.

Odpowiedz

4

Spróbuj ustawić opcję pipefail. Zwraca ostatnie polecenie potoku, który się nie powiódł. Jeden przykład:

Najpierw wyłączyć go:

set +o pipefail 

Tworzenie perl skrypt (script.pl) do testowania rurociągu:

#!/usr/bin/env perl 

use warnings; 
use strict; 

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

uruchomić w linii poleceń:

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

To daje:

0 Line3 

Wydaje się słuszne, niech zobaczyć z pipefail włączoną:

set -o pipefail 

i uruchom polecenie:

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

że plony:

255 Line3 
+0

Dzięki, to działa. Nie wiedziałem o "pipefail". – jerry

+0

Działa to również w sh. Dziękuję Ci. – AnkurTank

2

Moje rozwiązanie używał FIFO i bash "coproc" wbudowany, aby uzyskać komunikaty i status z każdej com mand w rurze. Nigdy wcześniej nie używałem fifosów. (oh, następnym razem używam BashEclipse na Fedorze). Zmienił się w ogólny mechanizm zarządzania dowolnymi poleceniami potoku. Rozwiązałem problem, ale nie w 10 lub 20 liniach kodu. więcej jak 200 na solidne rozwiązanie wielokrotnego użytku (zajęło mi to trzy dni).

dzielę moje notatki:

* 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 

Oszczędza $PIPESTATUS tablicę sub-shell na koniec dnia $a a następnie wydobywa go za pomocą jakiegoś powłoki zmienną substring removal; zobacz dłuższy przykład i opis, który podałem Get exit codes of a pipe when output is assigned to variable (Command Substitution).