110 votes

Empêcher grep de renvoyer une erreur lorsque l'entrée ne correspond pas

Je veux écrire dans un script bash un morceau de code qui vérifie si un programme est déjà en cours d'exécution. J'ai ce qui suit pour rechercher si bar est en cours d'exécution

 foo=`ps -ef | grep bar | grep -v grep`

Le

 grep -v grep

partie est pour s'assurer que le "grep bar" n'est pas pris en compte dans les résultats de ps

Lorsque bar n'est pas en cours d'exécution, foo est correctement vide. Mais mon problème réside dans le fait que le script a

 set -e

qui est un indicateur pour terminer le script si une commande renvoie une erreur. Il s'avère que lorsque bar n'est pas en cours d'exécution, "grep -v grep" ne correspond à rien et grep renvoie une erreur. J'ai essayé d'utiliser -q ou -s mais sans succès.

Existe-t-il une solution à cela? Merci

94voto

Sean Points 15363

Bien sûr :

ps -ef | grep bar | { grep -v grep || true; }

Ou même :

ps -ef | grep bar | grep -v grep | cat

51voto

myrdd Points 1260

Réponse courte

Écrire

ps -ef | grep bar | { grep -v grep || test $? = 1; }

si vous utilisez set -e.

Si vous utilisez l'option pipefail de bash (set -o pipefail), n'oubliez pas d'appliquer la gestion des exceptions (||test) à chaque grep dans le pipeline:

ps -ef | { grep bar || test $? = 1; } | { grep -v grep || test $? = 1; }

Dans les scripts shell, je vous suggère d'utiliser la fonction utilitaire "catch-1-grep" (c1grep) :

c1grep() { grep "$@" || test $? = 1; }

Expliqué

Le statut de sortie de grep est 0, 1 ou 2 : [1]

  • 0 signifie qu'une ligne est sélectionnée
  • 1 signifie aucune ligne sélectionnée
  • 2 signifie qu'une erreur s'est produite

grep peut également renvoyer d'autres codes s'il est interrompu par un signal (par exemple 130 pour SIGINT).

Puisque nous voulons ignorer uniquement le statut de sortie de 1, nous utilisons test pour supprimer ce statut de sortie spécifique.

  • Si grep renvoie 0, test n'est pas exécuté.
  • Si grep renvoie 1, test est exécuté et renvoie 0.
  • Si grep renvoie toute autre valeur, test est exécuté et renvoie 1.

Dans le dernier cas, le script se terminera immédiatement en raison de set -e ou set -o pipefail. Cependant, si vous ne vous souciez pas du tout des erreurs de grep, vous pouvez bien sûr écrire

ps -ef | grep bar | { grep -v grep || true; }

comme suggéré par Sean.


Utilisation supplémentaire dans les scripts shell

Dans les scripts shell, si vous utilisez beaucoup le grep, je vous suggère de définir une fonction utilitaire :

# Wrapper "catch exit status 1" de grep
c1grep() { grep "$@" || test $? = 1; }

De cette manière, votre pipeline redeviendra court et simple, sans perdre les fonctionnalités de set -e et set -o pipefail :

ps -ef | c1grep bar | c1grep -v grep

FYI :

  • Je l'ai appelé c1grep pour souligner qu'il capture simplement le statut de sortie 1, rien d'autre.
  • J'aurais pu appeler la fonction grep à la place (grep() { env grep "$@" ...; }), mais je préfère un nom moins confus et plus explicite, c1grep.

Utilisation supplémentaire de ps + grep

Donc, si vous voulez savoir comment éviter grep -v grep ou même la partie | grep de ps|grep tout en un, consultez certaines des autres réponses ; mais c'est quelque peu hors sujet à mon avis.


[1] Documentation de grep (spécification POSIX, docs GNU grep, page de manuel FreeBSD)

16voto

glenn jackman Points 69748

Un bon truc pour éviter grep -v grep est le suivant:

ps -ef | grep '[b]ar'

Cette expression régulière ne correspond qu'à la chaîne "bar". Cependant, dans la sortie de ps, la chaîne "bar" n'apparaît pas avec le processus grep.


Avant que je découvre pgrep, j'ai écrit cette fonction pour automatiser la commande ci-dessus:

psg () { 
    local -a patterns=()
    (( $# == 0 )) && set -- $USER
    for arg do
        patterns+=("-e" "[${arg:0:1}]${arg:1}")
    done
    ps -ef | grep "${patterns[@]}"
}

Ensuite,

psg foo bar

se transforme en

ps -ef | grep -e '[f]oo' -e '[b]ar'

10voto

Sorpigal Points 10412

Pourquoi demander à ps de fournir de grandes quantités de sortie avec -ef si vous allez jeter 99% de celle-ci? ps et surtout la version GNU est un couteau suisse de fonctionnalités pratiques. Essayez ceci :

ps -C bar -o pid= 1>/dev/null

Ici je spécifie -o pid= juste parce que, mais en réalité c'est inutile puisque nous jetons de toute façon toute la sortie standard. Cela serait utile si vous vouliez connaître le PID réel en cours d'exécution, cependant.

ps renverra automatiquement un statut de sortie non nul si -C ne parvient pas à trouver quelque chose et zéro s'il trouve quelque chose. Vous pourriez simplement dire ceci :

ps -C bar 1>/dev/null && echo bar en cours d'exécution || echo bar non en cours d'exécution

Ou

if ps -C bar 1>/dev/null ; then
    echo bar en cours d'exécution
else
    echo bar non en cours d'exécution
fi

N'est-ce pas plus simple? Pas besoin de grep, pas deux fois, ni même une fois.

1voto

DigitalRoss Points 80400
foo=`ps -ef | grep bar | grep -v grep` || true

Translation:

foo=`ps -ef | grep bar | grep -v grep` || true

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X