42 votes

Comment fonctionne cette expression [t]ricky bracket dans grep ?

J'ai vu récemment cette commande en une ligne :

$ ps -ef | grep [f]irefox 

thorsen   16730     1  1 Jun19 ?        00:27:27 /usr/lib/firefox/firefox ...

Il semble donc renvoyer la liste des processus contenant "firefox" dans les données mais en excluant le processus grep lui-même, et semble donc approximativement équivalent à :

ps -ef |grep -v grep| grep firefox

Je ne comprends pas comment cela fonctionne cependant. J'ai consulté la page de manuel de grep et d'autres sources mais je n'ai pas trouvé d'explication.

Et pour ajouter au mystère, si je lance :

$ ps -ef | grep firefox  > data
$ grep [f]irefox data

thorsen   15820 28618  0 07:28 pts/1    00:00:00 grep --color=auto firefox
thorsen   16730     1  1 Jun19 ?        00:27:45 /usr/lib/firefox/firefox ....

le [t]rick semble cesser de fonctionner !

Quelqu'un ici saura ce qui se passe, j'en suis sûr.

Merci.

63voto

jokerdino Points 39764

L'expression entre crochets fait partie du shell bash (et d'autres shells également) modèle de classe de caractères de recherche de grep.

Le programme grep comprend par défaut les expressions régulières basiques POSIX. Avec cela, vous pouvez définir des classes de caractères. Par exemple, ps -ef | grep [ab9]irefox trouverait "airefox", "birefox", "9irefox" si elles existaient, mais pas "abirefox".

La commande grep [a-zA-Z0-9]irefox trouverait même tous les processus commençant exactement par une lettre ou un chiffre et se terminant par "irefox".

Ainsi, ps -ef | grep firefox recherche des lignes contenant firefox. Comme le processus grep lui-même contient "firefox", grep le trouve également. En ajoutant des [], nous recherchons uniquement la classe de caractères "[f]" (qui se compose uniquement de la lettre "f" et est donc équivalente à juste un "f" sans les crochets). L'avantage des crochets est maintenant que la chaîne "firefox" n'apparaît plus dans la commande grep. Par conséquent, grep lui-même ne figurera pas dans le résultat de grep.

Comme peu de personnes sont familières avec les crochets en tant que correspondance de classe de caractères et les expressions régulières en général, le deuxième résultat peut sembler un peu mystérieux.

Si vous souhaitez corriger le deuxième résultat, vous pouvez les utiliser ainsi:

ps -ef | grep [f]irefox  > data
grep firefox data

(Référence)

10voto

dotancohen Points 315

La raison est que la chaîne

grep firefox

correspond au motif firefox, mais la chaîne

grep [f]irefox

ne correspond pas au motif [f]irefox (qui est équivalent au motif firefox).

C'est pourquoi le premier grep correspond à sa propre ligne de commande de processus, tandis que le second ne le fait pas.

0voto

theone Points 136

La réponse de Daniel est impeccable, mais il y a une complication intéressante évoquée par la réponse (largement incorrecte) de jokerdino concernant l'échappement du shell.

Tout d'abord, remarquez que la sortie non filtrée de ps contiendra une ligne correspondant au processus grep lancé par votre shell. Si vous exécutez grep firefox au moment où vous lancez ps, vous le verrez dans la sortie :

$ ps
thorsen   15820 28618  0 07:28 pts/1    00:00:00 grep firefox
thorsen   23983     1  1 Jun19 ?        00:12:34 some other process ....

Si vous prenez ensuite la sortie de ps et la filtrez à travers ce processus grep — en faisant un grep sur la sortie de ps pour trouver des chaînes qui correspondent à l'expression régulière firefox — alors, eh bien, cette ligne correspondra !

$ ps | grep firefox
thorsen   15820 28618  0 07:28 pts/1    00:00:00 grep firefox
                                                      ^^^^^^^ Trouvé !

Mais si vous avez lancé grep avec des arguments qui ne correspondent pas à l'expression régulière que vous recherchez, alors cette ligne de sortie de ps ne correspondra pas à l'expression régulière.

$ ps | grep 'f[ij]refo*x'

La sortie non filtrée contiendra une ligne comme

thorsen   15820 28618  0 07:28 pts/1    00:00:00 grep f[ij]refo*x

mais la sortie filtrée ne le fera pas, car cette ligne ne contient pas de sous-chaînes correspondant à l'expression régulière f[ij]refo*x. (Cette ligne ne contient pas firefx, ni fjrefx, ni firefox, ni fjrefoox, ou...)

Mais, comme l'a souligné jokerdino, il peut aussi se passer autre chose ici ! Parce que les caractères entre crochets sont aussi magiques pour la plupart des shells. Lorsque vous écrivez

ls foo*.[ch]

le shell Bash regarde en fait quels fichiers sont disponibles dans votre répertoire de travail actuel et développe ce glob en, par exemple,

ls foo.c foobar.c foobar.h

Si vous ne voulez pas que le globbing du shell se produise, alors vous devez échapper les caractères spéciaux magiques *, [, ] ou les mettre entre guillemets simples :

$ ls foo*.[ch]
foo.c   foobar.c        foobar.h

$ ls 'foo*.[ch]'
ls: foo*.[ch]: Aucun fichier ou dossier de ce type

Le globbing devient également inopérant si Bash ne trouve aucun fichier correspondant dans le répertoire actuel :

$ rm foo*.[ch]
$ ls foo*.[ch]
ls: foo*.[ch]: Aucun fichier ou dossier de ce type

Donc, lorsque vous avez écrit

$ grep [f]irefox

sans guillemets simples, cela a fait que grep recherche des lignes correspondant à l'expression régulière [f]irefox précisément parce qu' il n'y avait pas de fichier correspondant au glob [f]irefox dans votre répertoire de travail actuel ! Cela n'a aucun rapport avec vos observations réelles, mais il est intéressant de noter que vous auriez pu observer le comportement suivant :

$ cd /usr
$ ps -ef | grep [f]irefox 
thorsen   16730     1  1 Jun19 ?        00:27:27 /usr/lib/firefox/firefox ....

$ cd /usr/lib
$ ps -ef | grep [f]irefox
thorsen   15820 28618  0 07:28 pts/1    00:00:00 grep --color=auto firefox
thorsen   16730     1  1 Jun19 ?        00:27:27 /usr/lib/firefox/firefox ....

Dans le deuxième cas ici, parce que le répertoire actuel contient une entrée nommée firefox, l'argument non entre guillemets [f]irefox est développé par Bash avant que grep ne le voie, et vous vous retrouvez à faire un grep sur l'expression régulière firefox au lieu de [f]irefox. La solution serait d'ajouter des guillemets simples :

$ ps -ef | grep '[f]irefox'
thorsen   16730     1  1 Jun19 ?        00:27:27 /usr/lib/firefox/firefox ....

Je recommande d'ajouter des guillemets simples autour de chaque argument à tout moment, cela inclut les "métacaractères du shell" tels que *, [, (, {, =, ,, ;, etc. — surtout les regex !

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X