Zapobiegaj opuszczaniu grep w przypadku nomatch

29

Ten skrypt nie echa „po”:

#!/bin/bash -e

echo "before"

echo "anything" | grep e # it would if I searched for 'y' instead

echo "after"
exit

Byłoby tak również, gdybym usunął -eopcję z linii shebang, ale chcę ją zachować, aby mój skrypt zatrzymał się w przypadku wystąpienia błędu. Nie uważam, że grep nie znalazł dopasowania jako błędu. Jak mogę zapobiec tak nagłemu opuszczeniu?

iago-lito
źródło
To spostrzeżenie przeznaczone wyłącznie do rozważenia. Być może należy ponownie przemyśleć logikę tego skryptu. Jeśli znalezienie łańcucha nie jest ważne, po co go szukać? Definicja grep jest taka, że ​​decyzje podejmuje się na podstawie obecności lub nieobecności łańcucha. Jeśli nie przejmujesz się w żaden sposób, nie jest to ważne. Wydaje się również, że -ezakładasz, że troszczysz się o to: tak bardzo, że każdy problem jest katastrofalny.
Andrew Falanga,
2
@AndrewFalanga Dbam o to w obu przypadkach, ponieważ analizuję zawartość, var=$(complex command | grep complex_pattern)która może być zerowa (w takim przypadku mój program nie powinien się zakończyć). Jest to po prostu spreparowany skrypt, który powoduje występowanie problemu. Nie ma tutaj metafizycznej czarnej dziury, prawda? ;)
iago-lito
Wiedząc teraz, że zamierzasz uchwycić wynik, wyjaśnia niektóre rzeczy. Jak przedstawiono, było to dla mnie mylące.
Andrew Falanga

Odpowiedzi:

33
echo "anything" | grep e || true

Wyjaśnienie:

$ echo "anything" | grep e
### error
$ echo $?
1
$ echo "anything" | grep e || true
### no error
$ echo $?
0
### DopeGhoti's "no-op" version
### (Potentially avoids spawning a process, if `true` is not a builtin):
$ echo "anything" | grep e || :
### no error
$ echo $?
0

„||” oznacza „lub”. Jeśli pierwsza część polecenia „nie powiedzie się” (co oznacza, że ​​„grep e” zwraca niezerowy kod wyjścia), to część po „||” jest wykonywany, kończy się powodzeniem i zwraca zero jako kod wyjścia ( truezawsze zwraca zero).

John N.
źródło
3
Nieco krótsza wersja tego samego, który się nie rozkręca /bin/trueto: command || :(więc w twoim przypadku, set -e; grep 'needle' haystack || :).
DopeGhoti,
1
@DopeGhoti, true jest wbudowany w niektóre powłoki (przynajmniej bash 4.3na RHEL)
iruvar
3
Niepoprawne, ponieważ jeśli pierwsze polecenie nie powiedzie się, ukryje błąd. Prawidłowe rozwiązanie powinno zwrócić wartość niezerową, jeśli pierwsze polecenie w potoku zakończy się niepowodzeniem.
sorin
11

Solidny sposób na bezpieczne i opcjonalne grep przesyłanie wiadomości:

echo something | grep e || [[ $? == 1 ]] ## print 'something', $? is 0
echo something | grep x || [[ $? == 1 ]] ## no output, $? is 0
echo something | grep --wrong-arg e || [[ $? == 1 ]] ## stderr output, $? is 1

Zgodnie z instrukcją posix kod wyjścia 1 oznacza brak wybranych linii, a> 1 oznacza błąd.

James ZM Gao
źródło
1
To powinna być zaakceptowana odpowiedź, ponieważ pomija tylko kod wyjścia ostrzegawczy (1), jeśli grep niczego nie znajdzie, ale przekazuje prawdziwe błędy (kody wyjścia> 1). Inne rozwiązania tutaj zawsze eliminują prawdziwe błędy, które zwykle są złe.
HaroldFinch
7

Inną opcją jest dodanie kolejnej komendy do potoku - takiej, która nie zawiedzie:

echo "anything" | grep e | cat

Ponieważ catjest to ostatnie polecenie w potoku, to status wyjścia cat, a nie opcji grep, będzie użyty do ustalenia, czy potok się nie powiódł.

roelvanmeer
źródło
4

Inna opcja:

...
set +e
echo "anything" | grep e
set -e
...
Jeff Schaller
źródło
3

Rozwiązanie

#!/bin/bash -e

echo "before"

echo "anything" | grep e || : # it would if I searched for 'y' instead

echo "after"
exit

Wyjaśnienie

set -e lub set -o errexit

Wyjdź natychmiast, jeśli potok (który może składać się z pojedynczego prostego polecenia ), listy lub polecenia złożonego (patrz SHELL GRAMMARwyżej), kończy się ze statusem niezerowym. Powłoka nie kończy działania, jeśli polecenie, które się nie powiedzie, jest częścią listy poleceń bezpośrednio po słowie kluczowym whilelub until, częścią testu po słowach zastrzeżonych iflub elifsłowem zastrzeżonym, częścią dowolnego polecenia wykonanego w a &&lub|| liście z wyjątkiem polecenia następującego po ostatnim &&lub ||dowolnym polecenie w potoku, ale ostatnie, lub jeśli wartość zwracana polecenia jest odwracana za pomocą!. Jeśli polecenie złożone inne niż podpowłoka zwraca stan niezerowy, ponieważ polecenie nie powiodło się podczas -eignorowania, powłoka nie kończy działania. Pułapka włączona podpowłoki wyżej) i może spowodować zamknięcie podpowłoki przed wykonaniem wszystkich poleceń w podpowłoce.ERR, jeśli jest ustawiony, jest wykonywany przed wyjściem powłoki. Ta opcja dotyczy środowiska powłoki i każdego środowiska podpowłoki osobno (patrzCOMMAND EXECUTION ENVIRONMENT

Plus :to polecenie bez efektu w Bash.

Cyker
źródło