Jak sprawdzić status wyjścia za pomocą instrukcji if

255

Zastanawiałem się, jaki byłby najlepszy sposób sprawdzenia statusu wyjścia w instrukcji if, aby uzyskać echo określonego wyniku.

Myślę o tym

if [ $? -eq 1 ]
then
   echo "blah blah blah"
fi

Mam również problem z tym, że instrukcja exit znajduje się przed instrukcją if po prostu dlatego, że musi mieć ten kod wyjścia. Wiem też, że robię coś złego, ponieważ wyjście oczywiście spowoduje wyjście z programu.

deadcell4
źródło
3
Dodaj swój pełny skrypt (lub przynajmniej szerszy zakres). W przeciwnym razie wydaje się to w porządku.
RedX
5
Jeśli potrzebujesz użyć kodu wyjścia z jakiegoś konkretnego wywołania programu w dwóch różnych miejscach, musisz go zachować - coś w stylusome_program; rc=$?; if [ ${rc} -eq 1 ] .... fi ; exit ${rc}
twalberg

Odpowiedzi:

262

Każde uruchamiane polecenie ma status wyjścia.

Ta kontrola sprawdza status wyjścia polecenia, które zakończyło się przed uruchomieniem tego wiersza.

Jeśli chcesz aby skrypt zakończył się, gdy ten test zwróci wartość true (poprzednie polecenie nie powiodło się), umieść exit 1(lub cokolwiek) wewnątrz tego ifbloku po echo.

To powiedziawszy, jeśli uruchamiasz komendę i chcesz przetestować jej dane wyjściowe za pomocą następującego, jest często prostsze.

if some_command; then
    echo command returned true
else
    echo command returned some error
fi

Lub odwrócić to użycie ! negacji

if ! some_command; then
    echo command returned some error
else
    echo command returned true
fi

Zauważ jednak, że żadna z nich nie obchodzi, jaki jest kod błędu. Jeśli wiesz, że zależy Ci tylko na określonym kodzie błędu, musisz to sprawdzić $?ręcznie.

Etan Reisner
źródło
11
@ deadcell4 Gdy trzeba zakończyć skrypt powłoki w przypadku awarii programu, przydatny jest następujący idioma_command || return 1
gboffi
10
@gboffi returndziała tylko w funkcji i skrypcie źródłowym . Potrzebujesz exitdrugiego przypadku (który robi zbyt wiele w funkcji i skrypcie źródłowym). Ale tak, to z pewnością rozsądny wzorzec, jeśli nie potrzebujesz żadnego konkretnego czyszczenia ani dodatkowych wyników.
Etan Reisner
1
Muszę powiedzieć dash, że domyślna nieinteraktywna powłoka w wielu współczesnych dystrybucjach linuksa nie dba o rozróżnienie między wykonanymi skryptami powłoki returni exitwewnątrz nich. dashwychodzi ze skryptu, nawet jeśli go używam return.
gboffi
Jaka jest logika dwóch ostatnich kontroli? Wydaje się sprzeczne z intuicją, że warunek if <command>przechodzi, jeśli kod wyjścia wynosi 0. W innym języku byłoby odwrotnie
sjw
3
WAŻNA UWAGA: To nie zadziała w przypadku rur. if ! some_command | some_other_commandzignoruje status some_command. Dwa najbardziej obejścia poleceń to set -o pipefail(może zmienić funkcjonalność w innych częściach programu) lub przenieść ifinstrukcję if [[ ${PIPESTATUS[0]} -ne 0 ]]jako osobne polecenie uzupełniające (brzydkie, ale funkcjonalne). Jeśli używasz set -e, będziesz również chciał dodać || truena końcu potoku podczas korzystania z drugiego rozwiązania, ponieważ usunięcie rury z oferowanego przez nią przepływu kontrolnego ifspowodowałoby natychmiastowe wyjście.
Alex Jansen
186

Zauważ, że kody wyjścia! = 0 są używane do zgłaszania błędów. Więc lepiej zrobić:

retVal=$?
if [ $retVal -ne 0 ]; then
    echo "Error"
fi
exit $retVal

zamiast

# will fail for error codes > 1
retVal=$?
if [ $retVal -eq 1 ]; then
    echo "Error"
fi
exit $retVal
Oo.oO
źródło
Musisz przetestować na retVal, ponieważ $? po przypisaniu retVal nie jest wartością zwracaną z polecenia.
anr78
Niezupełnie: mywiki.wooledge.org/BashFAQ/002 - zgadzam się jednak, że edycja poprawia czytelność.
Oo.oO,
1
Właśnie znalazłem ten post, który wyjaśnia go stackoverflow.com/questions/20157938/...
anr78
dnf check-updatezwraca 0 (brak aktualizacji), 100 (dostępne aktualizacje) lub 1 (błąd).
jww
1
@jww - cóż, nie jest to dobry pomysł, aby być sprzecznym z konwencją ( gnu.org/software/bash/manual/html_node/Exit-Status.html ). Ale cóż, nic nie stoi na przeszkodzie. Jeśli dnfprogramiści wybrali ten sposób, to ich wybór. Ale ich wybór nie powoduje, że specyfikacja jest zepsuta :)
Oo.oO
44

$?jest parametrem jak każdy inny. Możesz zapisać jego wartość do użycia przed ostatecznym wywołaniem exit.

exit_status=$?
if [ $exit_status -eq 1 ]; then
    echo "blah blah blah"
fi
exit $exit_status
chepner
źródło
29

Alternatywa dla jawnej if oświadczenia

Minimalnie:

test $? -eq 0 || echo "something bad happened"

Kompletny:

EXITCODE=$?
test $EXITCODE -eq 0 && echo "something good happened" || echo "something bad happened"; 
exit $EXITCODE
Catskul
źródło
13

Aby dodać do pomocnej i szczegółowej odpowiedzi :

Jeśli musisz wyraźnie sprawdzić kod wyjścia, lepiej użyć operatora arytmetycznego (( ... )), w ten sposób:

run_some_command
(($? != 0)) && { printf '%s\n' "Command exited with non-zero"; exit 1; }

Lub użyj caseoświadczenia:

run_some_command; ec=$?  # grab the exit code into a variable so that it can
                         # be reused later, without the fear of being overwritten
case $ec in
    0) ;;
    1) printf '%s\n' "Command exited with non-zero"; exit 1;;
    *) do_something_else;;
esac

Powiązana odpowiedź na temat obsługi błędów w Bash:

codeforester
źródło