Expression régulière utilisant \\ vs utilisant \

10

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?

Wyatt Grant
la source
J'avais pensé que bash prendrait \\\.et donnerait du grep \.mais ce n'est pas le cas. bonne question

Réponses:

9

Tout d'abord, notez que la barre oblique correspond trop:

$ echo $'eegg \n e.g.' | grep e\.g\.
eegg
 e.g.

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:

$ echo $'eegg \n e.g.' | grep e\\.g\\.
 e.g.
$ echo $'eegg \n e.g.' | grep e\\\.g\\\.
 e.g.
$ echo $'eegg \n e.g.' | grep e\\\\.g\\\\.
$

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:

$ echo 'eegg
e.g.
e\.g\.' |  grep e\\\\.g\\\\.
e\.g\.

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:

$ echo \. \\. \\\. \\\\.
. \. \. \\.

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.

$ echo '\. \\. \\\. \\\\.'  # Note single-quotes
\. \\. \\\. \\\\.
John1024
la source
Question: Il faut deux barres obliques inverses pour que bash le considère comme une barre oblique inverse (l'une est la séquence d'échappement, l'autre est la barre oblique inversée littérale). Donc, quand il y en a 3, bash traite-t-il également le troisième retardateur comme une séquence d'échappement? Puisqu'il n'échappe à rien, est-il alors jeté?
Franz Kafka
@DanielAmaya Le troisième est traité comme une évasion pour le personnage qui suit. Dans notre cas, ce caractère est la période et, pour bash (contrairement à grep), une période échappée n'est qu'une période simple. bash passe ensuite la période de plaine à grep.
John1024
@DanielAmaya Voir la réponse mise à jour pour une echodéclaration qui illustre ce que bash fait dans ces cas.
John1024
2
@DanielAmaya Dans les deux cas, bash réduit les deux premières barres obliques à une seule barre oblique. Ce qui reste est \.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.
John1024
1
Juste un petit ajout - l'utilisation echon'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 comme printf "%s" ...est probablement la meilleure façon.
jimmij
4

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ème e\.g\.(des points), un quatrième e\,g\,(des comas) et une -ooption de grep pour imprimer uniquement les parties correspondantes.

  • Dans le cas suivant, faites .correspondre n'importe quel caractère (remarquez ''autour e.g., j'y reviendrai plus tard)

    $ grep -o 'e.g.' <<< grep -o 'e.g.' <<< 'this is an e.g. e,g, e\.g\. e\,g\,'
    e.g.
    e,g,
  • Ensuite, nous nous échappons .avec une barre oblique inverse \, donc seul le littéral .sera mis en correspondance:

    $ grep -o 'e\.g\.' <<< 'this is an e.g. e,g, e\.g\. e\,g\,'
    e.g.
  • 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):

    $ grep -o 'e\\.g\\.' <<< 'this is an e.g. e,g, e\.g\. e\,g\,'
    e\.g\.
    e\,g\,
  • Mais si nous ne voulons faire correspondre que \.non, \,une autre \est nécessaire pour échapper à la signification particulière du point:

    $ grep -o 'e\\\.g\\\.' <<< 'this is an e.g. e,g, e\.g\. e\,g\,'
    e\.g\.

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:

grep 'e\.g\.'     => grep e\\.g\\.
grep 'e\\.g\\.'   => grep e\\\\.g\\\\.  (each backslash has to be quoted separately)
grep 'e\\\.g\\\.' => grep e\\\\\\.g\\\\\\. (3 x 2 = 6 backslashes in total)
jimmij
la source
3

Lorsque vous faites un grep e\.g\., le shell consomme la barre oblique inverse, donc vous faites un grep e.g., qui correspond. Lorsque vous faites un grep e\\.g\\., le shell consomme à nouveau une barre oblique, et maintenant vous faites un grep 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 un grep e\\\.g\\\., il finit toujours par l'être grep 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\\\\.\\\\gfinit donc par l'être grep 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")

Franz Kafka
la source
0

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 bashpuis par grep.

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ère
  • echo \\: \- caractère spécial échappé donne le caractère
  • echo \\\a: \a- combinaison spéciale, ordinaire
  • echo \\\\: \\- combinaison spéciale, spéciale

echoaffichera la chaîne résultante après l'avoir bashinterprétée. Plus d' informations: documentation bash , les pirates bash wiki , spécifications POSIX .

.n'a pas de signification particulière dans bash. 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, bashvous 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. Exemple
echo 'part1'\''part2':: part1'part2

Regex dans grep

\est un personnage d'échappement avec une signification similaire à celle de bash. .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 comme aou.
  • \.- ne correspond que .littéralement

Vos 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 par bashla grep. Ensuite, après avoir grepé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"
    en'importe quel caractère gn'importe quel caractère - correspond e.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"
    en'importe quel caractère gn'importe quel caractère - correspond e.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 - correspond e.g.uniquement
  • grep e\\\.g\\\. <<< "this is an e.g. wow"
    grep 'e\.g\.' <<< "this is an e.g. wow"
    e.g.littéralement - correspond e.g.uniquement
  • grep e\\\\.g\\\\. <<< "this is an e.g. wow"
    grep 'e\\.g\\.' <<< "this is an e.g. wow"
    e\n'importe quel caractère g\n'importe quel caractère - ne correspond pase.g.
pabouk
la source