Pourquoi
grep e\\.g\\. <<< "this is an e.g. wow"
et
grep e\.g\. <<< "this is an e.g. wow"
faire la même chose?
Si j'ajoute une troisième barre oblique, elle a également le même résultat. MAIS, une fois que j'ai ajouté une quatrième barre oblique, cela ne fonctionne plus. Cela a à voir avec une question d'un ancien examen pour une classe. Il a demandé si celui avec deux barres obliques inverses fonctionnerait pour sortir la ligne avec "par exemple". Au départ, je pensais que cela ne fonctionnerait pas, mais j'ai essayé de m'en assurer et c'est ce qui s'est produit. Quelle est l'explication?
bash
shell
regular-expression
quoting
Wyatt Grant
la source
la source
\\\.
et donnerait du grep\.
mais ce n'est pas le cas. bonne questionRéponses:
Tout d'abord, notez que la barre oblique correspond trop:
En ce qui concerne Bash , une période échappée est identique à une période. Bash passe la période à grep . Pour grep, un point correspond à n'importe quoi.
Maintenant, considérez:
Lorsque Bash voit une double barre oblique, il la réduit à une seule barre oblique et la transmet à grep qui, dans le premier des trois tests ci-dessus, voit, comme nous le voulons, une seule barre oblique avant un point. Ainsi, cela fait la bonne chose.
Avec une triple barre oblique, Bash réduit les deux premiers à une seule barre oblique. Il voit alors
\.
. Puisqu'une période échappée n'a pas de signification particulière pour Bash, elle est réduite à une période simple. Le résultat est que grep voit, comme nous voulons, une barre oblique avant une période.Avec quatre barres obliques, Bash réduit chaque paire en une seule barre oblique. Bash passe à grep deux slashs et un point. grep voit les deux barres obliques et un point et réduit les deux barres obliques à une seule barre oblique littérale . À moins que l'entrée n'ait une barre oblique suivie d'un caractère, il n'y a pas de correspondance.
Pour illustrer ce dernier, rappelez-vous que dans les guillemets simples, tous les caractères sont littéraux. Ainsi, étant donné les trois lignes d'entrée suivantes, la commande grep correspond uniquement sur la ligne avec la barre oblique littérale dans l'entrée:
Résumé du comportement de Bash
Pour Bash, les règles sont
Deux barres obliques sont réduites à une seule barre oblique.
Une barre oblique devant un caractère normal, comme un point, n'est que le caractère normal (point).
Donc:
Il existe un moyen simple d'éviter toute cette confusion: sur la ligne de commande Bash, les expressions régulières doivent être placées entre guillemets simples. Dans des guillemets simples, Bash laisse tout seul.
la source
echo
déclaration qui illustre ce que bash fait dans ces cas.\.
ou.
. Pour bash, les deux sont les mêmes: ils équivalent à une période simple. Par conséquent, au total, ce que bash fournit à grep est le même pour les deux: une simple barre oblique suivie d'un point.echo
n'est pas un moyen très fiable de tester l'expression rationnelle en raison de nombreuses implémentations de ce programme. Par exemple sous mon zsh (écho intégré)echo \. \\. \\\. \\\\. \\\\\.
donne. \. \. \. \.
, mais/bin/echo \. \\. \\\. \\\\. \\\\\.
retourne. \. \. \\. \\.
. Quelque chose commeprintf "%s" ...
est probablement la meilleure façon.La sortie est la même que pour votre chaîne, mais en général, ces expressions régulières font des choses différentes. Modifions un peu votre exemple en ajoutant un deuxième motif
e,g,
(avec des comas), un troisièmee\.g\.
(des points), un quatrièmee\,g\,
(des comas) et une-o
option de grep pour imprimer uniquement les parties correspondantes.Dans le cas suivant, faites
.
correspondre n'importe quel caractère (remarquez''
autoure.g.
, j'y reviendrai plus tard)Ensuite, nous nous échappons
.
avec une barre oblique inverse\
, donc seul le littéral.
sera mis en correspondance:Mais nous pouvons échapper
\
avec un autre\
, de sorte que le littéral\
sera mis en correspondance suivi par.
(c'est-à-dire n'importe quel caractère):Mais si nous ne voulons faire correspondre que
\.
non,\,
une autre\
est nécessaire pour échapper à la signification particulière du point:Maintenant, comme vous n'avez pas utilisé l'
''
argument around grep, vous devez ajouter une autre barre oblique inverse pour échapper aux barres obliques inverses de l'interprétation du shell, donc:la source
Lorsque vous faites un
grep e\.g\.
, le shell consomme la barre oblique inverse, donc vous faites ungrep e.g.
, qui correspond. Lorsque vous faites ungrep e\\.g\\.
, le shell consomme à nouveau une barre oblique, et maintenant vous faites ungrep e\.\g.
, qui correspond à nouveau. Maintenant, une barre oblique inverse à la coquille ressemble\\
. Donc, quand vous l'avez\\
, la première est une séquence d'échappement, la seconde est une barre oblique inverse littérale. Quand vous faites ungrep e\\\.g\\\.
, il finit toujours par l'êtregrep e\.\g.
, car il n'y a pas de séquence d'échappement (\
) avant le premier\
pour en faire un littéral\
. Gardez à l'esprit \ est une barre oblique inverse,grep e\\\\.\\\\g
finit donc par l'êtregrep e\\.g\\.
, ce qui ne correspond évidemment pas.Pour voir comment le shell voit ce que vous faites, utilisez echo (par exemple,
echo grep e\\.g\\. <<< "this is an e.g. wow"
vs.echo grep e\\\\.g\\\\. <<< "this is an e.g. wow"
)la source
Les deux commandes produisent la même sortie uniquement pour votre entrée, mais sinon elles sont différentes. Pour comprendre ce qui se passe, nous devons savoir comment le paramètre est interprété d'abord par
bash
puis pargrep
.S'échapper en bash
\
est un caractère spécial qui annule la signification spéciale du caractère suivant, y compris\
lui-même. Si le caractère suivant n'a pas de signification particulière, il est transmis sans modification. Exemples avec commande et résultat:echo \a
:a
- caractère ordinaire échappé donne le caractèreecho \\
:\
- caractère spécial échappé donne le caractèreecho \\\a
:\a
- combinaison spéciale, ordinaireecho \\\\
:\\
- combinaison spéciale, spécialeecho
affichera la chaîne résultante après l'avoirbash
interprétée. Plus d' informations: documentation bash , les pirates bash wiki , spécifications POSIX ..
n'a pas de signification particulière dansbash
. C'est un caractère ordinaire pour la coquille. Voici les séquences pertinentes pour vos exemples:echo .
:.
echo \.
:.
echo \\.
:\.
echo \\\.
:\.
echo \\\\.
:\\.
Solution plus simple pour les chaînes littérales dans bash
Pour passer des paramètres littéralement,
bash
vous pouvez utiliser l''
échappement entre guillemets simples . Entre les guillemets simples, vous n'avez pas à vous soucier de la signification spéciale des caractères, car les guillemets simples sont le seul caractère ayant une signification spéciale. Vous pouvez insérer un guillemet simple après avoir entouré la première partie de la chaîne. Exempleecho 'part1'\''part2'
::part1'part2
Regex dans grep
\
est un personnage d'échappement avec une signification similaire à celle debash
..
est un caractère spécial qui représente une occurrence unique de n'importe quel caractère . Voir: regex POSIX , regex de GNU . Exemples d'expressions regex:.
- correspond à n'importe quel caractère commea
ou.
\.
- ne correspond que.
littéralementVos exemples
Sur la deuxième ligne de chaque exemple ci - dessous , vous trouverez équivalent avec des guillemets simples
'
montrant quelle chaîne littérale est passée parbash
lagrep
. Ensuite, après avoirgrep
échappé, le seul caractère spécial possible dans les exemples.
correspond à n'importe quel caractère. Sur la troisième ligne, il y a une description de ce que l'expression correspond.grep e.g. <<< "this is an e.g. wow"
grep 'e.g.' <<< "this is an e.g. wow"
e
n'importe quel caractèreg
n'importe quel caractère - corresponde.g.
et éventuellement d'autres chaînes commeeagb
grep e\.g\. <<< "this is an e.g. wow"
grep 'e.g.' <<< "this is an e.g. wow"
e
n'importe quel caractèreg
n'importe quel caractère - corresponde.g.
et éventuellement d'autres chaînes commeexgy
grep e\\.g\\. <<< "this is an e.g. wow"
grep 'e\.g\.' <<< "this is an e.g. wow"
e.g.
littéralement - corresponde.g.
uniquementgrep e\\\.g\\\. <<< "this is an e.g. wow"
grep 'e\.g\.' <<< "this is an e.g. wow"
e.g.
littéralement - corresponde.g.
uniquementgrep e\\\\.g\\\\. <<< "this is an e.g. wow"
grep 'e\\.g\\.' <<< "this is an e.g. wow"
e\
n'importe quel caractèreg\
n'importe quel caractère - ne correspond pase.g.
la source