Empêcher la sortie de grep en cas de nomatch

29

Ce script ne fait pas écho "après":

#!/bin/bash -e

echo "before"

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

echo "after"
exit

Il en serait de même si je supprimais l' -eoption sur la ligne shebang, mais je souhaite la conserver pour que mon script s'arrête en cas d'erreur. Je ne considère pas que grep ne trouve aucune correspondance comme une erreur. Comment puis-je l'empêcher de sortir si brusquement?

iago-lito
la source
Il s'agit d'une observation destinée uniquement à être considérée. Peut-être que la logique de ce script devrait être repensée. S'il n'est pas important de trouver la chaîne, pourquoi la rechercher? La définition de grep est telle que l'on prend des décisions basées sur la présence ou l'absence d'une chaîne. Si vous ne vous en souciez pas, ce n'est pas important. De plus, il semblerait que -evous vous en souciez: à tel point que tout problème est catastrophique.
Andrew Falanga
2
@AndrewFalanga Je me soucie de toute façon car j'analyse en fait le contenu var=$(complex command | grep complex_pattern)qui peut être nul (auquel cas mon programme ne devrait pas se terminer). Il s'agit simplement d'un script réduit qui fait que le problème se produit. Pas de trou noir métaphysique dans la logique ici, non? ;)
iago-lito
Savoir maintenant que vous aviez l'intention de capturer la sortie clarifie certaines choses. Tel que présenté, cela m'a dérouté.
Andrew Falanga

Réponses:

33
echo "anything" | grep e || true

Explication:

$ 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

Le "||" signifie "ou". Si la première partie de la commande "échoue" (ce qui signifie "grep e" renvoie un code de sortie non nul), la partie après le "||" est exécuté, réussit et renvoie zéro comme code de sortie ( truerenvoie toujours zéro).

John N
la source
3
Une version légèrement plus courte du même qui ne tourne pas /bin/trueest: command || :(donc dans votre cas, set -e; grep 'needle' haystack || :).
DopeGhoti
1
@DopeGhoti, trueest intégré dans certains shells (au moins bash 4.3sur RHEL)
iruvar
3
Non valide car si la première commande échoue, elle masquera l'erreur. Une solution correcte doit retourner non nulle si la première commande du tube échoue.
sorin
11

Un moyen robuste d' envoyer des grep messages en toute sécurité et en option :

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

Selon le manuel posix , le code de sortie 1 signifie qu'aucune ligne n'est sélectionnée et> 1 signifie une erreur.

James ZM Gao
la source
1
Cela devrait être la réponse acceptée, car elle ne supprime le code de sortie d'avertissement (1) que si grep ne trouve rien, mais elle transmet de vraies erreurs (codes de sortie> 1). Les autres solutions ici suppriment toujours les vraies erreurs, ce qui est généralement mauvais.
HaroldFinch
7

Une autre option consiste à ajouter une autre commande au pipeline - une qui n'échoue pas:

echo "anything" | grep e | cat

Étant donné qu'il cats'agit désormais de la dernière commande du pipeline, c'est l'état de sortie de cat, et non de grep, qui sera utilisé pour déterminer si le pipeline a échoué ou non.

roelvanmeer
la source
4

Une autre option:

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

Solution

#!/bin/bash -e

echo "before"

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

echo "after"
exit

Explication

set -e ou set -o errexit

Quittez immédiatement si un pipeline (qui peut consister en une seule commande simple ), une liste ou une commande composée (voir SHELL GRAMMARci - dessus), se termine avec un état différent de zéro. Le shell ne se ferme pas si la commande qui échoue fait partie de la liste de commandes immédiatement après un mot clé whileor until, une partie du test après les mots ifou elifréservés, une partie de toute commande exécutée dans une liste &&ou ||sauf la commande suivant la finale &&ou ||, tout dans un pipeline mais le dernier, ou si la valeur de retour de la commande est inversée avec!. Si une commande composée autre qu'un sous-shell renvoie un état différent de zéro car une commande a échoué alors qu'elle -eétait ignorée, le shell ne se ferme pas. Une interruption ERR, si elle est définie, est exécutée avant la fermeture du shell. Cette option s'applique à l'environnement shell et à chaque environnement sous-shell séparément (voir COMMAND EXECUTION ENVIRONMENTci - dessus), et peut entraîner la fermeture des sous-shell avant d'exécuter toutes les commandes du sous-shell.

De plus, :c'est la commande sans effet dans Bash.

Cyker
la source