Comment trouver des modèles sur plusieurs lignes à l'aide de grep?
Je veux trouver les fichiers qui ont "abc" ET "efg" dans cet ordre, et ces deux chaînes sont sur des lignes différentes dans ce fichier. Par exemple: un fichier avec le contenu:
blah blah..
blah blah..
blah abc blah
blah blah..
blah blah..
blah blah..
blah efg blah blah
blah blah..
blah blah..
Doit être adapté.
- double possible de Comment puis-je rechercher une multiligne motif dans un fichier?
Vous devez vous connecter pour publier un commentaire.
Grep n'est pas suffisant pour cette opération.
pcregrep qui se trouve dans la plupart des modernes systèmes Linux peut être utilisé comme
où -M, --multiligne permettent de modèles pour correspondre à plus d'une ligne
Il s'agit d'une nouvelle pcre2grep aussi. Les deux sont fournis par le PCRE projet.
pcre2grep est disponible pour Mac OS X via Mac Ports dans le cadre de port
pcre2
:et via Homebrew comme:
ou pour pcre2
-M
pour?abc
à la dernière ligne avecefg
. comment puis-je lui dire de s'arrêter à la PREMIÈRE occurrence deefg
?'abc.*(\n|.)*?efg'
.*
->'abc(\n|.)*?efg'
pour faire de la regex plus court (et pour être pédant)([misc]|\n)*
aide.pcregrep
. Merci!pcregrep
rendre les choses plus facile, maisgrep
sera trop de travail. Voir, par exemple, stackoverflow.com/a/7167115/123695pcre
.buffer-size
(quelque chose de ridicule comme--buffer-size=1024000
) avant de finalement fonctionné pour moiJe ne suis pas sûr si c'est possible avec grep, mais sed, c'est très facile:
sed
, mais si n'ai jamais vu une telle expression avant./efg/
de sortie?Voici une solution inspirée par cette réponse:
si " abc " et " efg " peuvent être sur la même ligne:
si " abc " et " efg " doivent être sur des lignes différentes:
Params:
-z
Traiter comme un ensemble de lignes, chacune terminée par un zéro octets au lieu d'un retour à la ligne. c'est à dire grep menaces à l'entrée comme une grande ligne.-l
imprimer le nom de chaque fichier d'entrée à partir de la sortie qui aurait normalement été imprimé.(?s)
activer PCRE_DOTALL, ce qui signifie que '.' trouve n'importe quel caractère ou de saut de ligne.l
. Autant que je sache, il n'y a pas de numéro de-1
option.-z
spécifie les options de grep pour traiter les retours à la ligne commezero byte characters
alors pourquoi avons-nous besoin de la(?s)
dans la regex ? Si c'est déjà un non-caractère de saut de ligne, ne devrait pas.
être en mesure de correspondre directement?sed devrait suffire comme affiche LJ indiqué ci-dessus,
au lieu de !d vous pouvez simplement utiliser p pour imprimer:
Je m'appuyais beaucoup sur pcregrep, mais avec de nouveaux grep vous n'avez pas besoin d'installer pcregrep pour plusieurs de ses caractéristiques. Utilisez simplement
grep -P
.Dans l'exemple de l'OP de la question, je pense que l'une des options suivantes fonctionnent bien, avec le deuxième meilleur appariement de la façon dont je comprends la question:
J'ai copié le texte comme /tmp/test1 et supprimé le " g " et enregistré dans /tmp/test2. Voici le résultat en montrant que la première montre le correspondant de la chaîne et la seconde affiche uniquement le nom de fichier (type -o est de montrer match et typiques de-l pour afficher uniquement le nom de fichier). Notez que le " z " est nécessaire pour multiligne et l' '(.|\n) " signifie pour correspondre 'rien d'autre que le saut de ligne' ou 'newline' - c'est à dire n'importe quoi:
Pour déterminer si votre version est assez nouveau, exécutez
man grep
et voir si quelque chose de semblable à ce qui apparaît près du haut:Qui est de GNU grep 2.10.
Cela peut être fait facilement en utilisant d'abord
tr
pour remplacer les retours à la ligne avec un autre caractère:Ici, je suis à l'aide de l'alarme de caractère,
\a
ASCII (7) au lieu d'un retour à la ligne.Ce n'est presque jamais trouvé dans votre texte, et
grep
pouvez le faire correspondre avec un.
, ou de correspondre, en particulier avec\a
.\0
et donc nécessairegrep -a
et d'appariement sur\x00
... Vous m'avez aidé à simplifier!echo $log | tr '\n' '\0' | grep -aoE "Error: .*?\x00Installing .*? has failed\!" | tr '\0' '\n'
est maintenantecho $log | tr '\n' '\a' | grep -oE "Error: .*?\aInstalling .*? has failed\!" | tr '\a' '\n'
grep -o
.awk one-liner:
abc
à travers à la fin du fichier si la fin n'est pas présent dans le fichier, ou le dernier se terminant le patron est absent. Vous pouvez corriger cela, mais cela va compliquer le script plutôt de manière significative./efg/
de sortie?Vous pouvez le faire très facilement si vous pouvez utiliser Perl.
Vous pouvez le faire avec une seule expression régulière, mais qui implique la prise de la totalité du contenu du fichier dans une chaîne unique, qui pourrait finir par prendre trop de mémoire avec de gros fichiers.
Pour être complet, voici la méthode:
.*?
) pour obtenir minimale de match.Je ne sais pas comment je ferais avec grep, mais je voudrais faire quelque chose comme ceci avec awk:
Vous devez être prudent lorsque vous faites cela, cependant. Voulez-vous la regex pour correspondre à la sous-chaîne ou la totalité de la parole? ajouter \w balises appropriées. Aussi, bien que cela se conforme strictement à la façon dont vous avez exprimé exemple, il n'est pas tout à fait quand abc apparaît une deuxième fois après l'efg. Si vous souhaitez gérer, ajouter un cas comme dans l' /abc/cas etc.
Malheureusement, vous ne pouvez pas. À partir de la
grep
docs:J'ai publié un grep alternative il y a quelques jours qui prend en charge directement, soit par correspondance multi-ligne ou en utilisant des conditions - j'espère que c'est utile pour certaines personnes, la recherche ici. C'est ce que les commandes pour l'exemple ressemblerait à:
Multiligne:
sift -lm 'abc.*efg' testfile
Conditions:
sift -l 'abc' testfile --followed-by 'efg'
Vous pouvez aussi spécifier que " efg "a suivre" abc " dans un certain nombre de lignes:
sift -l 'abc' testfile --followed-within 5:'efg'
Vous pouvez trouver plus d'informations sur sift-tool.org.
Tandis que le sed option est la plus simple et la plus facile, LJ du one-liner n'est malheureusement pas le plus portable. Ceux qui sont bloqués avec une version du C Shell aurez besoin pour échapper à leur la frange:
Cela ne fonctionne malheureusement pas en bash et coll.
Si vous êtes prêt à utiliser contextes, ceci pourrait être réalisé en tapant
Cela permet d'afficher tout entre "abc" et "efg", tant qu'ils sont à moins de 500 lignes les unes des autres.
Si vous avez besoin de les deux mots sont proches les uns des autres, par exemple pas plus de 3 lignes, vous pouvez le faire:
Même exemple mais en filtrant uniquement *.fichiers txt:
Et également vous pouvez remplacer
grep
de commande avecegrep
de commande si vous souhaitez également trouver avec des expressions régulières.vous pouvez utiliser grep incase vous n'êtes pas vif dans la séquence du motif.
exemple
grep -l
trouverez tous les fichiers qui correspond au premier modèle, et xargs sera grep pour le deuxième modèle. Espérons que cette aide.Avec d'argent à la recherche:
similaire pour le porteur d'anneau de réponse, mais avec ag à la place. La vitesse des avantages de l'argent chercheur pourrait briller ici.
(echo abctest; echo efg)|ag 'abc.*(\n|.)*efg'
ne correspond pasJ'ai utilisé cela pour en extraire une séquence fasta de multi-fasta fichier à l'aide de l'option-P de la commande grep:
grep -Pzo ">tig00000034[^>]+" file.fasta > desired_sequence.fasta
-P pour perl base de recherches
-z pour faire une fin de ligne de 0 octets plutôt que le saut de ligne char
-o de simplement capturer ce qui correspondait depuis grep renvoie l'ensemble de la ligne (qui, dans ce cas, puisque vous n'avez -z est l'ensemble du fichier).
Le noyau de la regexp est de la
[^>]
qui se traduit par "pas plus que le symbole"Comme une alternative à Balu Mohan réponse, il est possible de faire respecter l'ordre des motifs à l'aide de seulement
grep
,head
ettail
:Celui-ci n'est pas très jolie, bien que. Formaté plus lisible:
Cela permettra d'imprimer les noms de tous les fichiers où
"pattern2"
apparaît après"pattern1"
, ou où figurent toutes deux sur la même ligne:Explication
tail -n +i
- imprimer toutes les lignes après lai
th, inclusivegrep -n
- prepend en correspondance des lignes avec leurs numéros de lignehead -n1
- imprimer uniquement la première lignecut -d : -f 1
impression de la première coupe de la colonne à l'aide de:
comme délimiteur2>/dev/null
- silencetail
sortie d'erreur se produit si le$()
expression renvoie videgrep -q
- silencegrep
et de revenir immédiatement si une correspondance est trouvée, puisque nous sommes seulement intéressés par le code de sortie&>
? Je l'utilise aussi, mais je n'ai jamais vu documenté n'importe où. BTW, pourquoi devons-nous le silence grep de cette façon, en fait?grep -q
de ne pas faire l'affaire?&>
dit bash pour rediriger à la fois la sortie standard et l'erreur standard, voir la REDIRECTION dans le manuel de bash. Vous êtes très bon dans ce que nous pourrions tout aussi bien fairegrep -q ...
au lieu degrep ... &>/dev/null
, bonne prise!Cela devrait fonctionner aussi?!
$ARGV
contient le nom du fichier en cours lors de la lecture defile_list
modificateur de recherche à travers le saut de ligne./s
La filepattern
*.sh
est important de prévenir les répertoires d'être inspecté. Bien sûr, certains tests peuvent empêcher que trop.La
recherches à un maximum de 1 correspondance et des rendements (-n) le linenumber.
Si une correspondance a été trouvée (test-n ...), le dernier match de l'efg (trouver tous les et prendre le dernier avec tail-n 1).
d'autre continue.
Puisque le résultat est quelque chose comme
18:foofile.sh String alf="abc";
nous avons besoin de réduire l'écart à partir de ":" jusqu'à la fin de la ligne.Doit retourner un résultat positif si le dernier match de la 2ème expression est passé le premier match de la première.
Puis nous rapportons le nom du fichier
echo $f
.Si vous avez une estimation de la distance entre les 2 chaînes de caractères " abc " et " efg " vous cherchez, vous pouvez utiliser:
De cette façon, la première grep sera de retour à la ligne avec le " abc " plus #num1 lignes après, et #num2 lignes après elle, et la deuxième grep au crible tous ceux pour obtenir le 'efg'.
Alors vous saurez à quels fichiers ils apparaissent ensemble.
Cela devrait fonctionner:
Si il n'y a plus d'un match, vous pouvez filtrer à l'aide de grep -v