Verhindern, dass grep im Falle eines Nomatches beendet wird

29

Dieses Skript gibt kein "after" zurück:

#!/bin/bash -e

echo "before"

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

echo "after"
exit

Es würde auch funktionieren, wenn ich die -eOption in der Shebang-Zeile entferne , aber ich möchte sie behalten, damit mein Skript bei einem Fehler stoppt. Ich betrachte grep, das keine Übereinstimmung gefunden wird, nicht als Fehler. Wie kann ich verhindern, dass es so plötzlich beendet wird?

iago-lito
quelle
Dies ist eine Beobachtung, die nur zur Überlegung gedacht ist. Vielleicht sollte die Logik dieses Skripts noch einmal überlegt werden. Wenn es nicht wichtig ist, die Zeichenfolge zu finden, warum danach suchen? Die Definition von grep ist so, dass man Entscheidungen basierend auf dem Vorhandensein oder Fehlen einer Zeichenkette trifft. Wenn es dir egal ist, ist es nicht wichtig. Es scheint auch, dass -eSie sich darum kümmern, dass jedes Problem katastrophal ist.
Andrew Falanga
2
@AndrewFalanga Das ist mir egal, da ich gerade analysiere, ob der Inhalt var=$(complex command | grep complex_pattern)null ist (in diesem Fall sollte mein Programm nicht beendet werden). Dies ist nur ein abgekochtes Skript, das das Problem verursacht. Kein metaphysisches Schwarzes Loch in der Logik, oder? ;)
iago-lito
Wenn Sie jetzt wissen, dass Sie die Ausgabe erfassen möchten, werden einige Dinge klarer. Wie dargestellt, war es für mich verwirrend.
Andrew Falanga

Antworten:

33
echo "anything" | grep e || true

Erläuterung:

$ 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

Das "||" bedeutet "oder". Wenn der erste Teil des Befehls "failed" (was bedeutet, dass "grep e" einen Exit-Code ungleich Null zurückgibt) ist der Teil nach dem "||" wird ausgeführt, ist erfolgreich und gibt Null als Exit-Code zurück (gibt trueimmer Null zurück).

John N
quelle
3
Eine etwas kürzere Version desselben, die sich nicht dreht, /bin/trueist: command || :(in Ihrem Fall also set -e; grep 'needle' haystack || :).
DopeGhoti
1
@ DopeGhoti, trueist in einigen Shells eingebaut (zumindest bash 4.3auf RHEL)
iruvar
3
Nicht gültig, da der Fehler ausgeblendet wird, wenn der erste Befehl fehlschlägt. Eine korrekte Lösung sollte ungleich Null zurückgeben, wenn der erste Befehl in der Pipe fehlschlägt.
Sorin
11

Eine robuste Möglichkeit, Nachrichten sicher und optional zu grep versenden:

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

Exit-Code 1 bedeutet laut posix-Handbuch , dass keine Zeilen ausgewählt sind, und> 1 bedeutet, dass ein Fehler vorliegt.

James ZM Gao
quelle
1
Dies sollte die akzeptierte Antwort sein, da sie den Warnungs-Exit-Code (1) nur unterdrückt, wenn grep nichts findet, aber echte Fehler weitergibt (Exit-Codes> 1). Die anderen Lösungen hier unterdrücken immer echte Fehler, was normalerweise schlecht ist.
HaroldFinch
7

Eine andere Möglichkeit besteht darin, der Pipeline einen weiteren Befehl hinzuzufügen, der nicht fehlschlägt:

echo "anything" | grep e | cat

Da cates sich nun um den letzten Befehl in der Pipeline handelt, wird der Beendigungsstatus von catund nicht von verwendet grep, um zu bestimmen, ob die Pipeline fehlgeschlagen ist oder nicht.

Roelvanmeer
quelle
4

Andere Option:

...
set +e
echo "anything" | grep e
set -e
...
Jeff Schaller
quelle
3

Lösung

#!/bin/bash -e

echo "before"

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

echo "after"
exit

Erläuterung

set -e oder set -o errexit

Beenden Sie sofort, wenn eine Pipeline (die aus einem einzelnen einfachen Befehl bestehen kann ), eine Liste oder ein zusammengesetzter Befehl (siehe SHELL GRAMMARoben) mit einem Status ungleich Null beendet wird. Die Shell wird nicht beendet, wenn der fehlgeschlagene Befehl Teil der Befehlsliste ist, die unmittelbar auf ein whileoder until-Schlüsselwort folgt , Teil des Tests, der auf die ifoder elifreservierten Wörter folgt , Teil eines Befehls, der in einer &&oder ||-Liste ausgeführt wird, mit Ausnahme des Befehls, der auf das letzte &&oder ein ||beliebiges folgt Befehl in einer Pipeline vor dem letzten oder wenn der Rückgabewert des Befehls mit invertiert wird!. Wenn ein zusammengesetzter Befehl außer einer Subshell einen Status ungleich Null zurückgibt, weil ein Befehl fehlgeschlagen ist, während -eer ignoriert wurde, wird die Shell nicht beendet. Ein aktivierter Trap ERRwird ausgeführt, bevor die Shell beendet wird. Diese Option gilt für die Shell-Umgebung und jede Sub-Shell-Umgebung separat (siehe COMMAND EXECUTION ENVIRONMENToben) und kann dazu führen, dass Sub-Shells beendet werden, bevor alle Befehle in der Sub-Shell ausgeführt werden.

Plus, :ist der No-Effect-Befehl in Bash.

Cyker
quelle