grep
ne peut pas être alimenté en chaînes "brutes" lorsqu'il est utilisé à partir de la ligne de commande, car certains caractères doivent être échappés pour ne pas être traités comme des littéraux. Par exemple:
$ grep '(hello|bye)' # WON'T MATCH 'hello'
$ grep '\(hello\|bye\)' # GOOD, BUT QUICKLY BECOMES UNREADABLE
J'utilisais printf
pour échapper automatiquement les chaînes:
$ printf '%q' '(some|group)\n'
\(some\|group\)\\n
Cela produit une version échappée par bash de la chaîne, et en utilisant des raccourcis, cela peut facilement être passé à un appel grep:
$ grep `printf '%q' '(a|b|c)'`
Cependant, cela n'est clairement pas destiné à cela: certains caractères dans la sortie ne sont pas échappés, et certains le sont inutilement. Par exemple:
$ printf '%q' '(^#)'
\(\^#\)
Le caractère ^
Ne doit pas être échappé lorsqu'il est passé à grep
.
Existe-t-il un outil cli qui prend une chaîne brute et retourne ne version échappée par bash de la chaîne qui peut être directement utilisée comme modèle avec grep? Comment puis-je y parvenir en pure bash, sinon?
Si vous essayez d'obtenir que grep
utilise la syntaxe d'expression régulière étendue, la façon de procéder consiste à utiliser grep -E
(alias egrep
). Vous devez également connaître grep -F
(alias fgrep
) et, dans les versions plus récentes de GNU Coreutils, grep -P
.
Contexte: le grep
d'origine avait un assez petit ensemble d'opérateurs d'expression régulière; c'était l'implémentation d'expression régulière originale de Ken Thompson. Une nouvelle version avec un répertoire étendu a été développée plus tard, et pour des raisons de compatibilité, a obtenu un nom différent. Avec GNU grep
, il n'y a qu'un seul binaire, qui comprend la syntaxe RE traditionnelle de base si elle est invoquée comme grep
, et ERE si elle est invoquée comme egrep
. Certaines constructions de egrep
sont disponibles dans grep
en utilisant une barre oblique inverse pour introduire une signification spéciale.
Par la suite, le langage de programmation Perl a encore étendu le formalisme; ce dialecte regex semble être ce que la plupart des nouveaux arrivants s'attendent à tort à ce que grep
supporte également. Avec grep -P
, Cela fait; mais ce n'est pas encore largement pris en charge sur toutes les plateformes.
Ainsi, dans grep
, les caractères suivants ont une signification particulière: ^$[]*.\
Dans egrep
, les caractères suivants ont également une signification particulière: ()|+?{}
. (Les accolades pour la répétition n'étaient pas dans le egrep
d'origine.) Les parenthèses de regroupement permettent également des références arrières avec \1
, \2
, etc.
Dans de nombreuses versions de grep
, vous pouvez obtenir le comportement egrep
en mettant une barre oblique inverse avant les spéciaux egrep
. Il existe également des séquences spéciales comme \<\>
.
En Perl, un grand nombre d'évasions supplémentaires comme \w
\s
\d
ont été présenté. En Perl 5, la fonction d'expression régulière a été considérablement étendue, avec une correspondance non gourmande *?
+?
etc, parenthèses non groupées (?:...)
, l'anticipation, le lookbehinds, etc.
... Cela dit, si vous voulez vraiment convertir les expressions régulières egrep
en expressions régulières grep
sans invoquer aucun processus externe, essayez ${regex/pattern/substitution}
pour chacun des caractères spéciaux egrep
; mais reconnaissez que cela ne gère pas correctement les classes de caractères, les classes de caractères annulées ou les antislashs.
Si vous souhaitez rechercher une chaîne exacte,
grep -F '(some|group)\n' ...
-F
indique à grep
de traiter le modèle tel quel, sans interprétation comme une expression régulière.
(Ceci est également souvent disponible sous la forme fgrep
.)
Lorsque j'utilise grep -E avec des chaînes fournies par l'utilisateur, je leur échappe avec ceci
ere_quote() {
sed 's/[]\.|$(){}?+*^]/\\&/g' <<< "$*"
}
exemple d'exécution
ere_quote ' \ $ [ ] ( ) { } | ^ . ? + *'
# output
# \\ \$ \[ \] \( \) \{ \} \| \^ \. \? \+ \*
De cette façon, vous pouvez insérer en toute sécurité la chaîne entre guillemets dans votre expression régulière.
par exemple. si vous voulez trouver chaque ligne en commençant par le contenu utilisateur, l'utilisateur fournissant des chaînes amusantes comme. *
userdata=".*"
grep -E -- "^$(ere_quote "$userdata")" <<< ".*hello"
# if you have colors in grep you'll see only ".*" in red
Je pense que les réponses précédentes ne sont pas complètes car elles manquent une chose importante, à savoir la chaîne qui commence par un tiret (-). Donc, bien que cela ne fonctionne pas fonctionne:
echo "A-B-C" | grep -F "-B-"
Celui-ci:
echo "A-B-C" | grep -F -- "-B-"