Nombre de barres obliques inverses nécessaires pour échapper à la barre oblique inverse d'expression régulière sur la ligne de commande

12

J'ai récemment rencontré des problèmes avec certaines expressions rationnelles sur la ligne de commande et j'ai constaté que pour faire correspondre une barre oblique inverse, différents nombres de caractères peuvent être utilisés. Ce nombre dépend de la citation utilisée pour l'expression régulière (aucune, guillemets simples, guillemets doubles). Voir la session bash suivante pour ce que je veux dire:

echo "#ab\\cd" > file
grep -E ab\cd file
grep -E ab\\cd file
grep -E ab\\\cd file
grep -E ab\\\\cd file
#ab\cd
grep -E ab\\\\\cd file
#ab\cd
grep -E ab\\\\\\cd file
#ab\cd
grep -E ab\\\\\\\cd file
#ab\cd
grep -E ab\\\\\\\\cd file
grep -E "ab\cd" file
grep -E "ab\\cd" file
grep -E "ab\\\cd" file
#ab\cd
grep -E "ab\\\\cd" file
#ab\cd
grep -E "ab\\\\\cd" file
#ab\cd
grep -E "ab\\\\\\cd" file
#ab\cd
grep -E "ab\\\\\\\cd" file
grep -E 'ab\cd' file
grep -E 'ab\\cd' file
#ab\cd
grep -E 'ab\\\cd' file
#ab\cd
grep -E 'ab\\\\cd' file

Cela signifie que:

  • sans guillemets, je peux associer une barre oblique inverse à 4 à 7 barres obliques inverses réelles
  • avec des guillemets doubles, je peux associer une barre oblique inverse à 3-6 barres obliques inverses réelles
  • Avec des guillemets simples, je peux associer une barre oblique inverse à 2-3 barres obliques inverses réelles

Je comprends qu'une barre oblique inverse supplémentaire est ignorée par le shell (à partir de la page de manuel bash):

"Une barre oblique inversée (\) est le caractère d'échappement. Il conserve la valeur littérale du caractère suivant qui suit"

Cela ne s'applique pas aux exemples entre guillemets simples, car aucun échappement n'est effectué entre guillemets simples.

Et une barre oblique inverse supplémentaire est ignorée par la commande grep ("\ c" est juste "c" échappé, mais c'est exactement la même chose que "c", parce que "c" n'a pas de signification particulière dans une expression régulière).

Cela explique le comportement de l'exemple avec des guillemets simples, mais je ne comprends pas vraiment les deux autres exemples, en particulier pourquoi il y a une différence entre les chaînes non-qouted et les guillemets doubles.

Encore une fois, une citation de la page de manuel bash:

"L'insertion de caractères entre guillemets doubles préserve la valeur littérale de tous les caractères entre guillemets, à l'exception de $,`, \, et, lorsque l'expansion de l'historique est activée,!. "

J'ai essayé la même chose avec GNU awk (par exemple awk /ab\cd/{print} file), avec les mêmes résultats.

Perl, cependant, montre des résultats différents (en utilisant par exemple perl -ne "/ab\\cd/"\&\&print file):

  • sans guillemets, je peux associer une barre oblique inverse à 4-5 barres obliques inverses réelles
  • avec des guillemets doubles, je peux associer une barre oblique inverse à 3-4 barres obliques inverses réelles
  • Avec des guillemets simples, je peux associer une barre oblique inverse à 2 barres obliques inverses réelles

Quelqu'un peut-il expliquer cette différence entre des chaînes d'expression régulière non citées et à double qouted sur la ligne de commande pour grep et awk? Je ne suis pas très intéressé par une explication du comportement de Perl, car je n'utilise généralement pas de lignes simples Perl.

daniel kullmann
la source

Réponses:

10

Pour l'exemple non cité, chaque \\paire passe une barre oblique inverse à grep, donc 4 barres obliques inversées en transmettent deux à grep, ce qui se traduit par une seule barre oblique inverse. 6 barres obliques inverses passent trois à grep, se traduisant par une barre oblique inverse et une \c, qui est égale à c. Une barre oblique inverse supplémentaire ne change rien, car elle est traduite \c-> cpar le shell. Huit barres obliques inversées dans le shell sont quatre en grep, traduites en deux, donc cela ne correspond plus.

Pour l'exemple entre guillemets doubles, notez ce qui suit votre deuxième citation de la page de manuel bash:

La barre oblique inverse conserve sa signification spéciale uniquement lorsqu'elle est suivie par l'un des caractères suivants: $, `,", \ ou nouvelle ligne.

C'est-à-dire lorsque vous donnez un nombre impair de barres obliques inverses, la séquence se termine par \c, ce qui serait égal à cdans le cas non cité, mais lorsqu'elle est citée, la barre oblique inverse perd sa signification spéciale, elle \cest donc transmise à grep. C'est pourquoi la plage de barres obliques inverses "possibles" (c'est-à-dire celles qui composent un modèle correspondant à votre fichier d'exemple) diminue d'une unité.

Ansgar Esztermann
la source
... et puis il y a quelques bizarreries: par exemple: printf "\ntest"va insérer une nouvelle ligne avant "test", même si elle "\n"aurait dû être traduite "n"par le shell comme elle est entre guillemets ... (donc le résultat attendu devrait être, pour "\ ntest", "ntest". Nous devrions prendre l'habitude d'écrire: printf "\\ntest"ou printf '\ntest', mais d'une manière ou d'une autre je vois beaucoup de script s'appuyant sur l'étrange à la place.
Olivier Dulac
6

Ce lien décrit bash Quotes and Escaping

Votre question porte sur les trois premières sections.

  • Échappement par caractère
  • Citations faibles "guillemets doubles"
  • Citation forte «guillemets simples»
  • Citation de chaîne comme ANSI C
  • Cotation I18N / L10N (Internationalisation et localisation) .

Vous trouverez ci-dessous un tableau de la façon dont les chaînes au fur et à mesure les bashtransmettent grepet comment greples interpréter en interne.

Voyons d'abord echo "#ab\\cd" > file.
Dans les guillemets faibles ("") "#ab\\cd", le \\est un échappé \qui est passé à fileun seul littéral \. Donc, filecontient ab\cd

Maintenant, à vos commandes: Le tableau ci-dessous peut aider à voir ce qui se passe réellement avec chaque appel. Le *affiche ceux qui correspondent au contenu du fichier. Il s'agit vraiment d'appliquer les règles d'échappement de bash, comme sur la page Web, avec une note particulière à la réponse de Daniel Kullmann où il fait référence à un comportement d'évasion dans une situation de citation faible .

La barre oblique inverse conserve sa signification spéciale uniquement lorsqu'elle est suivie par l'un des caractères suivants: $, `,", \ ou nouvelle ligne.


                            bash passes    grep further
                            to grep        resolves to         
grep -E ab\cd file            abcd           abcd   
grep -E ab\\cd file           ab\cd          abcd  
grep -E ab\\\cd file          ab\cd          abcd
grep -E ab\\\\cd file         ab\\cd         ab\cd    * 
grep -E ab\\\\\cd file        ab\\\cd        ab\cd    *
grep -E ab\\\\\\cd file       ab\\\cd        ab\cd    *    
grep -E ab\\\\\\\cd file      ab\\\cd        ab\cd    *
grep -E ab\\\\\\\\cd file     ab\\\\cd       ab\\cd

grep -E "ab\cd" file          ab\cd          abcd
grep -E "ab\\cd" file         ab\cd          abcd
grep -E "ab\\\cd" file        ab\\cd         ab\cd    *
grep -E "ab\\\\cd" file       ab\\cd         ab\cd    *
grep -E "ab\\\\\cd" file      ab\\\cd        ab\cd    *
grep -E "ab\\\\\\cd" file     ab\\\cd        ab\cd    *
grep -E "ab\\\\\\\cd" file    ab\\\\cd       ab\\cd    

grep -E 'ab\cd' file          ab\cd          abcd  
grep -E 'ab\\cd' file         ab\\cd         ab\cd    *
grep -E 'ab\\\cd' file        ab\\\cd        ab\cd    *
grep -E 'ab\\\\cd' file       ab\\\\cd       ab\\cd
Peter.O
la source