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 du contenu:
blah blah..
blah blah..
blah abc blah
blah blah..
blah blah..
blah blah..
blah efg blah blah
blah blah..
blah blah..
Doit être apparié.
Réponses:
Grep n'est pas suffisant pour cette opération.
pcregrep qui se trouve dans la plupart des systèmes Linux modernes peut être utilisé comme
où
-M
,--multiline
autoriser les motifs à correspondre à plusieurs lignesIl existe également un nouveau pcre2grep . Les deux sont fournis par le projet PCRE .
pcre2grep est disponible pour Mac OS X via les ports Mac dans le cadre du port
pcre2
:et via Homebrew comme:
ou pour pcre2
pcre2grep est également disponible sur Linux (Ubuntu 18.04+)
la source
-M, --multiline
- Autorise les modèles à correspondre à plusieurs lignes.'abc.*(\n|.)*?efg'
.*
->'abc(\n|.)*?efg'
pour raccourcir l'expression régulière (et être pédant)pcregrep
rend les choses plus faciles, maisgrep
fonctionnera aussi. Par exemple, voir stackoverflow.com/a/7167115/123695Je ne sais pas si c'est possible avec grep, mais sed le rend très facile:
la source
sed
, mais je n'ai jamais vu une telle expression auparavant.Voici une solution inspirée de 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:
Paramètres:
-z
Traitez l'entrée comme un ensemble de lignes, chacune se terminant par un octet zéro au lieu d'une nouvelle ligne. c'est-à-dire que grep traite l'entrée comme une seule grande ligne.-l
nom d'impression de chaque fichier d'entrée à partir duquel la sortie aurait normalement été imprimée.(?s)
activer PCRE_DOTALL, ce qui signifie que '.' trouve n'importe quel caractère ou nouvelle ligne.la source
l
. AFAIK il n'y a pas d'-1
option numérique .-z
options spécifient grep pour traiter les sauts de ligne,zero byte characters
alors pourquoi avons-nous besoin de(?s)
dans l'expression régulière? S'il s'agit déjà d'un caractère autre que la nouvelle ligne, ne devrait-il pas.
pouvoir le faire correspondre directement?sed devrait suffire comme l'a indiqué LJ ci-dessus,
au lieu de! d, vous pouvez simplement utiliser p pour imprimer:
la source
Je me suis beaucoup appuyé sur pcregrep, mais avec le grep plus récent, vous n'avez pas besoin d'installer pcregrep pour la plupart de ses fonctionnalités. Utilisez simplement
grep -P
.Dans l'exemple de la question du PO, je pense que les options suivantes fonctionnent bien, avec la deuxième meilleure correspondance avec la façon dont je comprends la question:
J'ai copié le texte sous / tmp / test1 et supprimé le «g» et enregistré sous / tmp / test2. Voici la sortie montrant que la première affiche la chaîne correspondante et la seconde ne montre que le nom de fichier (typique -o est pour montrer la correspondance et typique -l est pour montrer seulement le nom de fichier). Notez que le «z» est nécessaire pour les multilignes et le «(. | \ N)» signifie correspondre à «autre chose que la nouvelle ligne» ou «nouvelle ligne» - c'est-à-dire n'importe quoi:
Pour déterminer si votre version est suffisamment nouvelle, exécutez
man grep
et voyez si quelque chose de similaire apparaît en haut:Cela vient de GNU grep 2.10.
la source
Cela peut être fait facilement en utilisant d'abord
tr
pour remplacer les sauts de ligne par un autre caractère:Ici, j'utilise le caractère d'alarme
\a
(ASCII 7) à la place d'une nouvelle ligne. Cela ne se trouve presque jamais dans votre texte etgrep
peut le faire correspondre avec un.
, ou le faire correspondre spécifiquement avec\a
.la source
\0
et donc j'avais besoin de faire desgrep -a
appariements\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:
la source
abc
fin à la fin du fichier si le motif de fin n'est pas présent dans le fichier ou si le dernier motif de fin est manquant. Vous pouvez résoudre ce problème, mais cela compliquera le script de manière assez significative./efg/
de la sortie?Vous pouvez le faire très facilement si vous pouvez utiliser Perl.
Vous pouvez également le faire avec une seule expression régulière, mais cela implique de prendre tout le contenu du fichier dans une seule chaîne, ce qui pourrait finir par occuper trop de mémoire avec des fichiers volumineux. Pour être complet, voici cette méthode:
la source
.*?
) pour obtenir une correspondance minimale.Je ne sais pas comment je ferais ça avec grep, mais je ferais quelque chose comme ça avec awk:
Cependant, vous devez faire attention à la façon dont vous procédez. Voulez-vous que l'expression régulière corresponde à la sous-chaîne ou au mot entier? ajoutez des balises \ w selon le cas. De plus, bien que cela soit strictement conforme à la façon dont vous avez indiqué l'exemple, cela ne fonctionne pas tout à fait lorsque abc apparaît une deuxième fois après efg. Si vous voulez gérer cela, ajoutez un si approprié dans le cas / abc / etc.
la source
Malheureusement, vous ne pouvez pas. De la
grep
documentation:la source
grep -Pz
Si vous êtes prêt à utiliser des contextes, cela peut être réalisé en tapant
Cela affichera tout entre "abc" et "efg", tant qu'ils sont à moins de 500 lignes les uns des autres.
la source
Si vous avez besoin que les deux mots soient proches l'un de l'autre, par exemple pas plus de 3 lignes, vous pouvez le faire:
Même exemple mais filtrant uniquement les fichiers * .txt:
Et vous pouvez également remplacer la
grep
commande par laegrep
commande si vous souhaitez également rechercher des expressions régulières.la source
Il y a quelques jours, j'ai publié une alternative à grep qui prend en charge cela directement, soit via une correspondance multiligne ou en utilisant des conditions - j'espère que cela sera utile pour certaines personnes qui recherchent ici. Voici à quoi ressembleraient les commandes de l'exemple:
Multiligne:
Conditions:
Vous pouvez également spécifier que 'efg' doit suivre 'abc' sur un certain nombre de lignes:
Vous pouvez trouver plus d'informations sur sift-tool.org .
la source
sift -lm 'abc.*efg' testfile
fonctionne, car la correspondance est gourmande et engloutit toutes les lignes jusqu'à la dernièreefg
du fichier.Bien que l'option sed soit la plus simple et la plus facile, la doublure monocoque de LJ n'est malheureusement pas la plus portable. Ceux qui sont coincés avec une version du C Shell devront échapper à leur frange:
Malheureusement, cela ne fonctionne pas dans bash et al.
la source
la source
vous pouvez utiliser grep au cas où vous ne souhaitez pas dans la séquence du motif.
exemple
grep -l
trouvera tous les fichiers qui correspondent au premier modèle, et xargs recherchera le deuxième modèle. J'espère que cela t'aides.la source
Avec chercheur d'argent :
similaire à la réponse du porteur de l'anneau, mais avec ag à la place. Les avantages de vitesse du chercheur d'argent pourraient éventuellement briller ici.
la source
(echo abctest; echo efg)|ag 'abc.*(\n|.)*efg'
ne correspond pasJ'ai utilisé cela pour extraire une séquence fasta d'un fichier multi fasta en utilisant l'option -P pour grep:
Le noyau de l'expression rationnelle est le
[^>]
qui se traduit par "pas plus grand que le symbole"la source
Comme alternative à la réponse de Balu Mohan, il est possible d'imposer l'ordre des modèles en utilisant uniquement
grep
,head
ettail
:Celui-ci n'est cependant pas très joli. Format plus lisible:
Cela affichera les noms de tous les fichiers où
"pattern2"
apparaît après"pattern1"
, ou où les deux apparaissent sur la même ligne :Explication
tail -n +i
- imprimer toutes les lignes après lei
e, inclusgrep -n
- ajouter des lignes correspondantes avec leurs numéros de lignehead -n1
- imprimer uniquement la première lignecut -d : -f 1
- imprimer la première colonne coupée en utilisant:
comme délimiteur2>/dev/null
-tail
sortie d'erreur de silence qui se produit si l'$()
expression retourne videgrep -q
- tairegrep
et retourner immédiatement si une correspondance est trouvée, car nous ne sommes intéressés que par le code de sortiela source
&>
? Je l'utilise aussi, mais je ne l'ai jamais vu documenté nulle part. BTW, pourquoi devons-nous faire taire grep de cette façon, en fait?grep -q
ne fera pas l'affaire aussi?&>
indique à bash de rediriger la sortie standard et l'erreur standard, voir REDIRECTION dans le manuel bash. Vous avez tout à fait raison en ce que nous pourrions tout aussi bien fairegrep -q ...
au lieu degrep ... &>/dev/null
, bonne prise!Cela devrait aussi marcher?!
$ARGV
contient le nom du fichier en cours lors de la lecture desfile_list /s
recherches de modificateurs sur la nouvelle ligne.la source
Le modèle de fichier
*.sh
est important pour empêcher l'inspection des répertoires. Bien sûr, un test pourrait également empêcher cela.le
recherche au maximum 1 correspondance et renvoie (-n) le numéro de lin. Si une correspondance a été trouvée (test -n ...) trouver la dernière correspondance de efg (trouver tout et prendre la dernière avec queue -n 1).
sinon continuer.
Puisque le résultat est quelque chose comme
18:foofile.sh String alf="abc";
nous devons couper de ":" jusqu'à la fin de la ligne.Devrait retourner un résultat positif si la dernière correspondance de la 2e expression est passée la première correspondance de la première.
Ensuite, nous rapportons le nom de fichier
echo $f
.la source
Pourquoi pas quelque chose de simple comme:
renvoie 0 ou un entier positif.
egrep -o (affiche uniquement les correspondances, astuce: plusieurs correspondances sur la même ligne produisent une sortie multiligne comme si elles se trouvaient sur des lignes différentes)
grep -A1 abc
(imprimer abc et la ligne après)grep efg | wc -l
(Nombre 0-n de lignes efg trouvées après abc sur la même ligne ou sur les lignes suivantes, le résultat peut être utilisé dans un "si")grep peut être changé en egrep etc. si une correspondance de modèle est nécessaire
la source
Si vous avez une estimation de la distance entre les 2 chaînes 'abc' et 'efg' que vous recherchez, vous pouvez utiliser:
De cette façon, le premier grep retournera la ligne avec les lignes 'abc' plus # num1 après, et # num2 lignes après, et le second grep passera en revue toutes ces lignes pour obtenir le 'efg'. Vous saurez alors dans quels fichiers ils apparaissent ensemble.
la source
Avec ugrep sorti il y a quelques mois:
Cet outil est hautement optimisé pour la vitesse. Il est également compatible GNU / BSD / PCRE-grep.
Notez que nous devons utiliser une répétition paresseuse
+?
, sauf si vous voulez faire correspondre toutes les lignes avecefg
jusqu'à la dernièreefg
du fichier.la source
Cela devrait fonctionner:
S'il y a plus d'une correspondance, vous pouvez filtrer en utilisant grep -v
la source