Jak sprawdzić, czy polecenie się powiodło?

234

Czy jest jakiś sposób, aby sprawdzić, czy wystąpił błąd podczas wykonywania polecenia?

Przykład:

test1=`sed -i "/:@/c connection.url=jdbc:oracle:thin:@$ip:1521:$dataBase" $search`
valid $test1

function valid () {
  if $test -eq 1; then
    echo "OK"
    else echo "ERROR" 
  fi
}

Próbowałem już to zrobić, ale wygląda na to, że to nie działa. Nie wiem jak to zrobić.

moata_u
źródło
9
Preferuj $ (foo) nad backticks `foo` , ponieważ możesz go zagnieździć i łatwiej odróżnić go od apostrofów.
użytkownik nieznany
1
BTW, musisz zdefiniować funkcję ( valid) przed jej wywołaniem.
wjandrea

Odpowiedzi:

344

Zwracana wartość jest przechowywana w $?. 0 oznacza sukces, inne oznaczają błąd.

some_command
if [ $? -eq 0 ]; then
    echo OK
else
    echo FAIL
fi

Jak każda inna wartość tekstowa, możesz przechowywać ją w zmiennej do przyszłego porównania:

some_command
retval=$?
do_something $retval
if [ $retval -ne 0 ]; then
    echo "Return code was not zero but $retval"
fi

Dla możliwych operatorów porównania, patrz man test.

Lekensteyn
źródło
Fajnie ... czy mogę zatrzymać błąd wyjściowy? !! , ponieważ w tym przypadku mam błąd 2: błąd polecenia „tekst” ex, nie znaleziono pliku i mój błąd „tekst” Który w tym przypadku nie powiódł się na przykład
moata_u
@moata_u: możesz zapisać wartość w zmiennej, jak pokazano w mojej odpowiedzi.
Lekensteyn,
@moata_u: Musisz wstawić ;przed elsei fi: `if ...; następnie ...; inaczej ...; fi
Lekensteyn,
4
Jest to bezużyteczne użycietest .
David Foerster,
1
@Blauhirn [ $? ](lub równoważnie test $?) nie działa, ponieważ zwraca $?liczbę (0, 1 itd.), Która nie jest równa null. Zapoznaj się z dokumentacjątest : „Jeśli WYRAŻENIE zostanie pominięte,„ test ”zwróci fałsz. Jeśli WYRAŻENIE jest pojedynczym argumentem,„ test ”zwróci fałsz, jeśli argument jest pusty, a prawda w przeciwnym razie.”
Lekensteyn,
87

Jeśli potrzebujesz tylko wiedzieć, czy polecenie zakończyło się powodzeniem, czy nie, nie zawracaj sobie głowy testowaniem $?, po prostu przetestuj polecenie bezpośrednio. Na przykład:

if some_command; then
    printf 'some_command succeeded\n'
else
    printf 'some_command failed\n'
fi

A przypisanie wyniku do zmiennej nie zmienia wartości zwracanej (cóż, chyba że zachowuje się inaczej, gdy standardowe wyjście nie jest oczywiście terminalem).

if output=$(some_command); then
    printf 'some_command succeded, the output was «%s»\n' "$output"
fi

http://mywiki.wooledge.org/BashGuide/TestsAndConditionals wyjaśnia ifbardziej szczegółowo.

geirha
źródło
Co zrobić, jeśli chcesz zrobić coś innego niż rozgałęzianie, np. Przechowywanie wartości logicznej tego, czy nie powiodło się w pliku .ini? Wtedy twoja zmienna wyjściowa tego nie robi, ale $?robi to, więc nie masz racji twierdząc, że bezpośrednie testowanie polecenia jest zdecydowanie lepsze.
Timothy Swan
Przypisanie wyjścia do zmiennej może zmienić wartość zwracaną, gdy ktoś użyje localpolecenia, tj. local foo=$(false); echo $?;Tworzy 0na wyjściu. Ale dotyczy localto tylko funkcji bash.
Cromax
26
command && echo OK || echo Failed
Abraham Sanchez
źródło
Jeśli commandzwraca błąd na ekranie, jak go ukryć?
Sigur
Jeśli commandwypisuje błędy do stderr, możesz użyć formularza command 2> /dev/null && echo OK || echo Failed. Do 2> /dev/nullprzekierowania stderr wyjście do /dev/null. Jednak niektóre narzędzia wypiszą błędy na standardowe wyjście (mimo że jest to zła praktyka). W takim przypadku możesz pominąć 2 z przekierowania, ale stracisz dane wyjściowe z polecenia. Ta metoda sprawdzania sukcesu jest dobra w przypadku prostej logiki trójskładnikowej, ale w przypadku bardziej złożonych zachowań najlepiej jest sprawdzić $?skuteczność polecenia lub użyć ifmetody blokowej opisanej w odpowiedzi @ geirha.
Bender the Greatest
17

$? powinien zawierać status wyjścia z poprzedniego polecenia, który powinien wynosić zero, bez błędu.

Coś w stylu;

cd /nonexistant
if [ $? -ne 0 ]
then
    echo failed
else
    echo success!
fi

w większości przypadków łatwiej jest używać konstruktora && do łączenia poleceń, które muszą być od siebie zależne. Tak cd /nonexistant && echo success!by nie powtórzyć sukces, ponieważ przerwy dowodzenia przed &&. Następstwem tego jest ||, gdzie cd /nonexistant || echo fail będzie echo nie dlatego cd powiodło się. (staje się to przydatne, jeśli użyjesz czegoś takiego jak || exit, co zakończy skrypt, jeśli poprzednie polecenie się nie powiedzie.)

Shaun
źródło
Fajnie ... czy mogę zatrzymać błąd wyjściowy? !! , ponieważ w tym przypadku mam błąd 2: błąd polecenia „tekst” ex, nie znaleziono pliku i mój błąd „tekst” Który w tym przypadku nie powiódł się na przykład
moata_u
@moata_u patrz mywiki.wooledge.org/BashFAQ/002
geirha
5
command && echo $? || echo $?
modrobert
źródło
Masz na myśli command; echo $??
Javier Buzzi
Nie, moja linia jest poprawna, podaje rzeczywisty kod wyniku jako liczbę, niezależnie od tego, czy sukces, czy porażka.
modrobert
1
Ok, proszę wyjaśnij mi różnice: true && echo $? || echo $?/ true; echo $?i false && echo $? || echo $?/ false; echo $?- przekonasz się, że robią dokładnie to samo: D
Javier Buzzi
Tak, ten sam wynik, ale bez warunku. Pierwotnie zadane pytanie brzmiało: „Jak sprawdzić, czy polecenie się powiodło?”, Opierało się na poprzednich odpowiedziach. command && echo "success: $?" || echo "fail: $?"
Wydaje
5

Należy zauważyć, że if...then...fii &&/ lub ||typ podejścia dotyczy statusu wyjścia zwracanego przez polecenie, które chcemy przetestować (0 w przypadku powodzenia); jednak niektóre polecenia nie zwracają niezerowego statusu wyjścia, jeśli polecenie zakończyło się niepowodzeniem lub nie mogło poradzić sobie z danymi wejściowymi. Oznacza to, że zwykłe ifi &&/ ||podejścia nie będą działać dla tych konkretnych poleceń.

Na przykład w systemie Linux GNU filenadal wychodzi z 0, jeśli otrzymał nieistniejący plik jako argument i findnie mógł zlokalizować określonego pliku użytkownika.

$ find . -name "not_existing_file"                                          
$ echo $?
0
$ file ./not_existing_file                                                  
./not_existing_file: cannot open `./not_existing_file' (No such file or directory)
$ echo $?
0

W takich przypadkach jednym potencjalnym sposobem na poradzenie sobie z sytuacją jest odczyt stderr/ stdinkomunikaty, np. Te, które zwróciły filepolecenie lub parsują dane wyjściowe polecenia jak w find. W tym celu casemożna użyć instrukcji.

$ file ./doesntexist  | while IFS= read -r output; do                                                                                                                  
> case "$output" in 
> *"No such file or directory"*) printf "%s\n" "This will show up if failed";;
> *) printf "%s\n" "This will show up if succeeded" ;;
> esac
> done
This will show up if failed

$ find . -name "doesn'texist" | if ! read IFS= out; then echo "File not found"; fi                                                                                     
File not found

(To jest odpowiedź mojej własnej odpowiedzi na powiązane pytanie na unix.stackexchange.com )

Sergiy Kolodyazhnyy
źródło
1

Jak wspomniano w wielu innych odpowiedziach, wystarczy prosty test $?tego typu

if [ $? -eq 0 ]; then something; fi

Jeśli chcesz sprawdzić, czy polecenie się nie powiodło , możesz użyć krótszej wersji w bash(ale być może przesadzonej) w następujący sposób:

if (($?)); then something; fi

Działa to przy użyciu (( ))trybu arytmetycznego, więc jeśli polecenie zwróciło success, tj. $? = 0Wtedy test oceni, do ((0))którego testu false, w przeciwnym razie wróci true.

Aby przetestować sukces , możesz użyć:

if ! (($?)); then something; fi

ale jest już niewiele krótszy niż pierwszy przykład.

afuna
źródło
1
konstruktywna krytyka?
afuna,