Comment faire un match non gourmand en grep?

Réponses:

285

Vous recherchez un match non gourmand (ou paresseux). Pour obtenir une correspondance non gourmande dans les expressions régulières, vous devez utiliser le modificateur ?après le quantificateur. Par exemple, vous pouvez changer .*en .*?.

Par défaut, grepil ne prend pas en charge les modificateurs non gourmands, mais vous pouvez utiliser grep -Ppour utiliser la syntaxe Perl.

Mark Byers
la source
3
Le modificateur eegg: dot all est également appelé multiligne. C'est un modificateur qui change le "." faire correspondre le comportement pour inclure les nouvelles lignes (normalement ce n'est pas le cas). Il n'y a pas de tel modificateur dans grep, mais il y en a dans pcregrep .
A. Wilson
1
Correction: Dans la plupart des versions de regex qui le supportent, le mode qui permet de .faire correspondre les nouvelles lignes est appelé DOTALL ou mode à une seule ligne ; Ruby est le seul à l'appeler multiligne . Dans les autres versions, le mode multiligne permet aux ancres ( ^et $) de correspondre aux limites de ligne. Ruby n'a pas de mode équivalent car dans Ruby, ils fonctionnent toujours de cette façon.
Alan Moore
5
-Pétait complètement nouveau pour moi, je me suis heureusement échappé pendant des années, et n'utilisais que -E... tant d'années gaspillées! - Note à moi-même: relisez les pages Man comme une chose (encore plus!) Régulière, vous ne digérez jamais assez de commutateurs et d'options.
ocodo
29
Sur certaines plates-formes (comme Mac OS X) grepne prend pas en charge -P, mais si vous utilisez, egrepvous pouvez utiliser le .*?modèle pour obtenir le même résultat. egrep -o 'start.*?end' text.html
SaltyNuts
4
En tant qu'extension du commentaire @SaltyNuts, Mac OS X ne prend pas en charge -Pmais -Eappellerait egrepdonc les .*?travaux suggérés très bien.
Fredrik Erlandsson
84

En fait, le .*?seul fonctionne dans perl. Je ne suis pas sûr de ce que serait la syntaxe équivalente de l'expression rationnelle étendue grep. Heureusement, vous pouvez utiliser la syntaxe perl avec grep, donc grep -Pcela fonctionnerait mais grep -Equi est le même que egrepcela ne fonctionnerait pas (ce serait gourmand).

Voir aussi: http://blog.vinceliu.com/2008/02/non-greedy-regular-expression-matching.html

John Smith
la source
9
grep -Pne fonctionne pas dans GNU grep 2.9 - il suffit de l'essayer (cela ne fait pas d'erreur, n'applique simplement pas silencieusement ?.) La classe not non plus, par exemple:env|grep '[^\=]*\='
roberto tomás
2
Il n'y a pas d' grep -Poption ou de pgrepcommande dans Darwin / OS X 10.8 Mountain Lion, maisegrep fonctionne très bien.
Steve HHH
2
Il y a une pgrepcommande sur ma boîte OS X 10.9, mais c'est un programme complètement différent dont le but est de "trouver ou signaler des processus par leur nom".
Desty
@ robertotomás Répondant à un commentaire d'un enfant de 6 ans ici, mais ... J'ai pensé cela aussi et j'ai réalisé que je recevais plusieurs matchs non gourmands. Par exemple, sur un terminal couleur, vous pouvez voir que `echo" bbbbb "| grep -P 'b. *? b'` renvoie 2 correspondances.
zzxyz
13

grep

Pour une correspondance non gourmande, grepvous pouvez utiliser une classe de caractères annulée. En d'autres termes, essayez d'éviter les caractères génériques.

Par exemple, pour récupérer tous les liens vers des fichiers jpeg à partir du contenu de la page, vous utiliseriez:

grep -o '"[^" ]\+.jpg"'

Pour traiter plusieurs lignes, dirigez d'abord l'entrée xargs. Pour la performance, utilisez ripgrep.

Kenorb
la source
12

Mon grep qui fonctionne après avoir essayé des choses dans ce fil:

echo "hi how are you " | grep -shoP ".*? "

Assurez-vous simplement d'ajouter un espace à chacune de vos lignes

(La mienne était une recherche ligne par ligne pour cracher des mots)

Jonz
la source
3
-shoPnice mnemonic :)
Mariusz
echo "bbbbb" | grep -shoP 'b.*?b'est un peu une expérience d'apprentissage. La seule chose qui a fonctionné pour moi en termes de paresseux explicite également.
zzxyz
3

La réponse courte utilise la prochaine expression régulière:

(?s)<car .*? model=BMW .*?>.*?</car>
  • (? s) - cela fait une correspondance sur plusieurs lignes
  • . *? - correspond à n'importe quel caractère, un certain nombre de fois de manière paresseuse (correspondance minimale)

Une réponse (un peu) plus compliquée est:

(?s)<([a-z\-_0-9]+?) .*? model=BMW .*?>.*?</\1>

Cela permet de faire correspondre car1 et car2 dans le texte suivant

<car1 ... model=BMW ...>
...
...
...
</car1>
<car2 ... model=BMW ...>
...
...
...
</car2>
  • (..) représente un groupe de capture
  • \ 1 dans ce contexte correspond au même texte correspondant le plus récemment en capturant le groupe numéro 1
jmc
la source
1

Désolé, j'ai 9 ans de retard, mais cela pourrait fonctionner pour les téléspectateurs en 2020.

Supposons donc que vous ayez une ligne comme "Hello my name is Jello". Vous voulez maintenant rechercher les mots qui commencent 'H'et se terminent par 'o', avec n'importe quel nombre de caractères entre les deux. Et nous ne voulons pas de lignes, nous voulons juste des mots. Donc pour cela on peut utiliser l'expression:

grep "H[^ ]*o" file

Cela renverra tous les mots. La façon dont cela fonctionne est que: Cela permettra à tous les caractères au lieu du caractère espace entre les deux, de cette façon nous pouvons éviter plusieurs mots dans la même ligne.

Vous pouvez maintenant remplacer le caractère d'espace par tout autre caractère de votre choix. Supposons que la ligne initiale était "Hello-my-name-is-Jello", alors vous pouvez obtenir des mots en utilisant l'expression:

grep "H[^-]*o" file
mr.1n5an_e
la source
-1

Je sais que c'est un peu un post mort mais je viens de remarquer que cela fonctionne. Il a supprimé à la fois le nettoyage et le nettoyage de ma sortie.

> grep -v -e 'clean\-\?up'
> grep --version grep (GNU grep) 2.20
user200850
la source