Zakończ skrypt powłoki na podstawie kodu zakończenia procesu

Mam skrypt powłoki, który wykonuje wiele poleceń. Jak sprawić, by skrypt powłoki zakończył działanie, jeśli którakolwiek z komend zakończy działanie z niezerowym kodem wyjścia?

 339
Author: Cerin, 2008-09-18

9 answers

Po każdym poleceniu kod wyjścia można znaleźć w zmiennej $?, więc będziesz miał coś w stylu:

ls -al file.ext
rc=$?; if [[ $rc != 0 ]]; then exit $rc; fi

Musisz uważać na polecenia piped, ponieważ $? daje Ci tylko kod powrotu ostatniego elementu w rurze, więc w kodzie:

ls -al file.ext | sed 's/^/xx: /"

Nie zwróci kodu błędu, jeśli plik nie istnieje (ponieważ sed część potoku działa, zwracając 0).

Powłoka bash faktycznie dostarcza tablicę, która może pomóc w tym przypadku, to jest PIPESTATUS. Tablica ta zawiera jeden element dla każdego z komponentów potoku, do którego można uzyskać dostęp indywidualnie, jak ${PIPESTATUS[0]}:

pax> false | true ; echo ${PIPESTATUS[0]}
1

Zauważ, że dzięki temu otrzymujesz wynik polecenia false, a nie cały potok. Możesz także przetworzyć całą listę według własnego uznania:

pax> false | true | false; echo ${PIPESTATUS[*]}
1 0 1

Jeśli chcesz uzyskać największy kod błędu z potoku, możesz użyć czegoś w stylu:

true | true | false | true | false
rcs=${PIPESTATUS[*]}; rc=0; for i in ${rcs}; do rc=$(($i > $rc ? $i : $rc)); done
echo $rc

Przechodzi przez każdy z PIPESTATUS elementów z kolei, przechowując go w rc jeśli była większa niż poprzednia wartość rc.

 433
Author: paxdiablo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-07 01:24:37

Jeśli chcesz pracować z $?, musisz to sprawdzić po każdym poleceniu, ponieważ $? jest aktualizowana po zakończeniu każdego polecenia. Oznacza to, że jeśli wykonasz potoki, otrzymasz tylko kod zakończenia ostatniego procesu w potoku.

Innym podejściem jest zrobienie tego:

set -e
set -o pipefail

Jeśli umieścisz to na górze skryptu powłoki, wygląda na to, że bash zajmie się tym za Ciebie. Jak zauważył poprzedni plakat, "set-e" spowoduje, że bash zakończy działanie z błędem w dowolnym prostym poleceniu. "set-o pipefail" spowoduje, że bash zakończy działanie z błędem również dla dowolnej komendy w potoku.

Zobacz tutaj lub tutaj dla trochę więcej dyskusji na ten problem. tutaj {[9] } znajduje się sekcja podręcznika bash na wbudowanym zestawie.

 207
Author: Jeff Hill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-18 06:20:55

"set -e" jest to prawdopodobnie najprostszy sposób, aby to zrobić. Po prostu umieść to przed poleceniami w programie.

 47
Author: Allen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-18 06:09:55

Jeśli wywołasz exit w bash bez parametrów, zwróci kod zakończenia ostatniego polecenia. W połączeniu z lub bash powinien wywoływać exit tylko wtedy, gdy poprzednie polecenie nie powiedzie się. Ale nie testowałem tego.

command1 || exit;
command2 || exit;

Bash będzie również przechowywać kod zakończenia ostatniego polecenia w zmiennej $?.

 27
Author: Arvodan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-18 06:11:37
[ $? -eq 0 ] || exit $?; # exit for none-zero return code
 23
Author: chemila,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-26 23:15:24

Http://cfaj.freeshell.org/shell/cus-faq-2.html#11

  1. Jak uzyskać kod wyjścia z cmd1 w cmd1|cmd2

    Po pierwsze, zauważ, że cmd1 kod wyjścia może być niezerowy i nadal nie oznacza błąd. Dzieje się tak na przykład w

    cmd | head -1
    

    Możesz zaobserwować 141 (lub 269 z ksh93) status wyjścia cmd1, ale to dlatego, że cmd został przerwany przez sygnał SIGPIPE, gdy head -1 zakończone po przeczytaniu jednej linijki.

    Aby poznać status wyjścia z elementów rurociągu cmd1 | cmd2 | cmd3

    A. z zsh:

    Kody zakończenia są dostarczane w specjalnej tablicy pipestatus. cmd1 kod wyjścia jest w $pipestatus[1], cmd3 kod wyjścia w $pipestatus[3], tak że $? jest zawsze taka sama jak $pipestatus[-1].

    B. z bashem:

    Kody wyjścia są podane w specjalnej tablicy PIPESTATUS. cmd1 kod wyjścia jest w ${PIPESTATUS[0]}, cmd3 kod wyjścia w ${PIPESTATUS[2]}, tak że $? jest zawsze taka sama jak ${PIPESTATUS: -1}.

    ...

    Więcej szczegóły patrz poniższy link .

 20
Author: gatoatigrado,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-17 19:37:11

Dla bash:

# this will trap any errors or commands with non-zero exit status
# by calling function catch_errors()
trap catch_errors ERR;

#
# ... the rest of the script goes here
#  

function catch_errors() {
   # do whatever on errors
   # 
   #
   echo "script aborted, because of errors";
   exit 0;
}
 19
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-18 06:18:53

W bashu jest to proste, po prostu połącz je z&&:

command1 && command2 && command3

Możesz również użyć zagnieżdżonego konstruktu if:

if command1
   then
       if command2
           then
               do_something
           else
               exit
       fi
   else
       exit
fi
 8
Author: Martin W,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-18 06:08:32
#
#------------------------------------------------------------------------------
# run a command on failure exit with message
# doPrintHelp: doRunCmdOrExit "$cmd"
# call by:
# set -e ; doRunCmdOrExit "$cmd" ; set +e
#------------------------------------------------------------------------------
doRunCmdOrExit(){
    cmd="$@" ;

    doLog "DEBUG running cmd or exit: \"$cmd\""
    msg=$($cmd 2>&1)
    export exit_code=$?

    # if occured during the execution exit with error
    error_msg="Failed to run the command:
        \"$cmd\" with the output:
        \"$msg\" !!!"

    if [ $exit_code -ne 0 ] ; then
        doLog "ERROR $msg"
        doLog "FATAL $msg"
        doExit "$exit_code" "$error_msg"
    else
        #if no errors occured just log the message
        doLog "DEBUG : cmdoutput : \"$msg\""
        doLog "INFO  $msg"
    fi

}
#eof func doRunCmdOrExit
 4
Author: Yordan Georgiev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-25 09:16:40