Bash ignoruje błąd dla konkretnego polecenia

444

Korzystam z następujących opcji

set -o pipefail
set -e

W skrypcie bash, aby zatrzymać wykonywanie po błędzie. Mam ~ 100 wierszy skryptu i nie chcę sprawdzać kodu powrotu każdej linii w skrypcie.

Ale w przypadku jednego konkretnego polecenia chcę zignorować błąd. Jak mogę to zrobić?

Vivek Goel
źródło

Odpowiedzi:

757

Rozwiązanie:

particular_script || true

Przykład:

$ cat /tmp/1.sh
particular_script()
{
    false
}

set -e

echo one
particular_script || true
echo two
particular_script
echo three

$ bash /tmp/1.sh
one
two

three nigdy nie zostanie wydrukowany.

Chcę również dodać, że gdy pipefailjest włączony, wystarczy, aby powłoka pomyślała, że ​​cały potok ma niezerowy kod wyjścia, gdy jedno z poleceń w potoku ma niezerowy kod wyjścia ( pipefailwyłączony musi być ostatnim) .

$ set -o pipefail
$ false | true ; echo $?
1
$ set +o pipefail
$ false | true ; echo $?
0
Igor Chubin
źródło
15
+1. Jak wyjaśniono w podręczniku Bash Reference Manual , „powłoka nie wychodzi”, gdy -eatrybut jest ustawiony ”, jeśli polecenie, które się nie powiedzie, jest częścią listy poleceń bezpośrednio po słowie kluczowym whilelub, untilsłowem kluczowym, testem w ifinstrukcji, częścią dowolnego wykonanego polecenia na liście &&lub ||poza poleceniem następującym po ostatnim &&lub ||dowolnym poleceniu w potoku, ale ostatnim, lub jeśli status powrotu polecenia jest odwracany za pomocą !. ”
ruakh
@IgorChubin Nie wiem dlaczego, ale to nie działa wyjście = (ldd $2/bin/* || true) | grep "not found" | wc -lskrypt kończy się po tej linii, gdy błąd powrotu ldd
Vivek Goel
1
(ldd $2/bin/* || true) | grep "not found" | wc -l || true
Igor Chubin
1
powodu set -o pipefail. gdy grepnic nie znajdzie, zwraca niezerowy kod wyjścia i wystarczy, że powłoka pomyśli, że cały potok ma niezerowy kod wyjścia.
Igor Chubin
3
Jeśli chcesz zachować wartość zwracaną (bez wychodzenia), spróbuj mycommand && true. Umożliwia to sprawdzenie kodu powrotu w kolejnych krokach i obsługę go programowo.
Ed Ost
179

Po prostu dodaj || truepo poleceniu, w którym chcesz zignorować błąd.

Lars Kotthoff
źródło
11
Myślę, że należy to dodać: ta metoda pozwoli na zachowanie kodu odpowiedzi i komunikatu o błędzie, podczas gdy „!” metoda opisana poniżej zmieni kod odpowiedzi, a tym samym nie generuje błędu. Jest to ważne, gdy używasz set -ei próbujesz uchwycić komunikat o błędzie: np.set -e; TEST=$(foo 2>&1 || true); echo $TEST
Spanky
87

Nie zatrzymuj się, a także zapisz status wyjścia

Na wszelki wypadek, jeśli chcesz, aby skrypt nie zatrzymywał się w przypadku niepowodzenia określonego polecenia, a także chcesz zapisać kod błędu nieudanego polecenia:

set -e
EXIT_CODE=0
command || EXIT_CODE=$?
echo $EXIT_CODE
Arslan Qadeer
źródło
2
Właśnie tego potrzebowałem
sarink
z jakiegoś powodu nie działa dla mnie ... no cóż
Jeef
EXIT_CODE nie jest ustawiane na zero, gdy polecenie zwraca 0. Jednak niezerowe kody wyjścia są przechwytywane. Masz pomysł, dlaczego?
Ankita13,
@ Ankita13 Ponieważ jeśli było zero, pierwszy commandbył udany i trzeba uruchomić to, co jest później ||. czytaj to tak: jeśli się commandnie powiedzie, zrób EXIT_CODE=$?. Prawdopodobnie możesz po prostu command || echo "$?"użyć lub użyć „pułapki”, aby uzyskać bardziej szczegółowe debugowanie. Zobacz to -> stackoverflow.com/a/6110446/10737630
tinnick
63

Bardziej zwięźle:

! particular_script

Ze specyfikacji POSIX dotyczącej set -e(mój nacisk):

Gdy ta opcja jest włączona, jeśli proste polecenie zawiedzie z jednego z powodów wymienionych w Konsekwencjach błędów powłoki lub zwraca wartość statusu wyjścia> 0 i nie jest częścią listy złożonej po pewnym czasie, do, lub jeśli słowo kluczowe, i nie jest częścią listy AND ani OR i nie jest potokiem poprzedzonym! słowo zastrzeżone , wówczas powłoka natychmiast wychodzi.

Lily Finley
źródło
16
To po prostu odwraca kod wyjścia polecenia, więc polecenie, które zakończyło się pomyślnie, zwróci 1 zamiast 0 i zakończy się niepowodzeniem ze skryptem -e.
Marboni
17
Rozumiem, że !zapobiegnie wyjściu pocisku bez względu na wszystko. Ten skrypt wyświetla komunikat Still alive!po uruchomieniu, wskazując, że skrypt został uruchomiony do końca. Czy widzisz inne zachowanie?
Lily Finley
7
masz rację, odwraca status wyjścia, ale nie powoduje awarii skryptu, gdy polecenie kończy się na 0 lub 1. Byłem nieuważny. W każdym razie, dziękuję za zacytowanie dokumentacji, włożyłem wyrażenie do ifklauzuli i rozwiązałem problem.
Marboni
42

Zamiast „zwracania prawdy” można również użyć narzędzia „noop” lub zerowego (jak podano w specyfikacji POSIX ) :i po prostu „nic nie robić”. Zaoszczędzisz kilka liter. :)

#!/usr/bin/env bash
set -e
man nonexistentghing || :
echo "It's ok.."
Timo
źródło
3
chociaż ||: nie jest tak jasne jak || prawda (co może wprowadzać w błąd użytkowników niebędących ekspertami), podoba mi się zwięzłość
David
Ten wariant nie zwraca tekstu, który może być ważny w CI.
kivagant
5

Jeśli chcesz zapobiec awarii skryptu i zebrać kod powrotu:

command () {
    return 1  # or 0 for success
}

set -e

command && returncode=$? || returncode=$?
echo $returncode

returncode jest gromadzony bez względu na to, czy polecenie się powiedzie, czy nie.

volingas
źródło
2

Korzystam z poniższego fragmentu podczas pracy z narzędziami CLI i chcę wiedzieć, czy jakiś zasób istnieje, czy nie, ale nie dbam o wynik.

if [ -z "$(cat no_exist 2>&1 >/dev/null)" ]; then
    echo "none exist actually exist!"
fi
Payman
źródło
1
Wydaje się, że to naprawdę okrągły i umiarkowanie drogi sposób na powiedzenieif [ -r not_exist ]
tripleee
1

Podoba mi się to rozwiązanie:

: `particular_script`

Wykonywane jest polecenie / skrypt między tylnymi tyknięciami, a jego dane wyjściowe są podawane do polecenia „:” (co jest odpowiednikiem „true”)

$ false
$ echo $?
1
$ : `false`
$ echo $?
0

edycja: Naprawiono brzydką literówkę

Q-life
źródło
0

chociaż || truejest preferowany, ale możesz także

var=$(echo $(exit 1)) # it shouldn't fail
Foto Blysk
źródło
0

Odtąd nie działały dla mnie żadne rozwiązania, więc znalazłem inne:

set +e
find "./csharp/Platform.$REPOSITORY_NAME/obj" -type f -iname "*.cs" -delete
find "./csharp/Platform.$REPOSITORY_NAME.Tests/obj" -type f -iname "*.cs" -delete
set -e

Jest to przydatne w przypadku CI i CD. W ten sposób komunikaty o błędach są drukowane, ale cały skrypt jest nadal wykonywany.

Konard
źródło
0
output=$(*command* 2>&1) && exit_status=$? || exit_status=$?
echo $output
echo $exit_status

Przykład użycia tego do utworzenia pliku dziennika

log_event(){
timestamp=$(date '+%D %T') #mm/dd/yy HH:MM:SS
echo -e "($timestamp) $event" >> "$log_file"
}

output=$(*command* 2>&1) && exit_status=$? || exit_status=$?

if [ "$exit_status" = 0 ]
    then
        event="$output"
        log_event
    else
        event="ERROR $output"
        log_event
fi
Robert.C
źródło
0

Dzięki za proste rozwiązanie tutaj z góry:

<particular_script/command> || true

Poniższa konstrukcja może być wykorzystana do dodatkowych działań / rozwiązywania problemów ze skryptami i dodatkowych opcji kontroli przepływu:

if <particular_script/command>
then
   echo "<particular_script/command> is fine!"
else
   echo "<particular_script/command> failed!"
   #exit 1
fi

Możemy zahamować dalsze działania i w exit 1razie potrzeby.

Almaz Gareev
źródło