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ć?

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

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.


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

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.


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.."

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.


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

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ę


0

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

var=$(echo $(exit 1)) # it shouldn't fail

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.


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

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.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.