Comment puis-je grep un répertoire pour les lignes qui contiennent "Foo", mais obtenir uniquement des correspondances lorsque la ligne suivante contient également "Bar"?
Comment puis-je grep un répertoire pour les lignes qui contiennent "Foo", mais obtenir uniquement des correspondances lorsque la ligne suivante contient également "Bar"?
Réponses:
@ warl0ck m'a pointé dans la bonne direction avec pcregrep, mais j'ai dit "contient", pas "est", et j'ai demandé un répertoire, pas un fichier.
Cela semble fonctionner pour moi.
pcregrep -rMi 'Foo(.*)\n(.*)Bar' .
Grep lui-même ne semble pas le supporter, utilisez plutôt pcregrep:
Foo
Bar
Foo
abc
pcregrep -M "Foo\nBar" file
Eu:
Foo
Bar
Fooet Barcomprendrait toute la ligne.
Avec un sedscript:
#!/bin/sed -nf
/^Foo/{
h # put the matching line in the hold buffer
n # going to nextline
/^Bar/{ # matching pattern in newline
H # add the line to the hold buffer
x # return the entire paragraph into the pattern space
p # print the pattern space
q # quit the script now
}
}
Pour l'utiliser :
chmod +x script.sed
printf '%s\n' * | ./script.sed
L' printfafficher ici tous les fichiers du répertoire courant sur une ligne chaque, et le transmettre à sed.
Remarque : ceci est trié par ordre alphabétique.
Plus d'infos utiles pattern spaceet hold space ICI .
grymoire.com a de très bonnes choses sur la shellprogrammation.
h, n, H, x, p, qsignifie Très intéressant.
pattern space& hold space: grymoire.com/Unix/Sed.html#uh-56 ou en français commentcamarche.net/faq/9536-sed-introduction-a-sed-part-i
En utilisant grepuniquement, vous pouvez construire le tuyau suivant:
grep -A1 'Foo' input_file | grep -B1 'Bar' | grep 'Foo'
Le premier grepobtiendra toutes les lignes qui contiennent Fooainsi que la ligne après le match. Ensuite, nous obtenons des lignes qui contiennent Barainsi que la ligne avant la correspondance, et enfin extraire les lignes de cette sortie qui contiennent Foo.
EDIT: Comme l' a souligné Manatwork , il y a quelques cas problématiques à observer. Bien qu'il s'agisse d'un défi intéressant, en raison de grepla fonctionnalité orientée ligne, toute solution avec elle est susceptible d'être un «hack» et vous feriez probablement mieux d'utiliser quelque chose comme pcregrepce qui est plus adapté à la tâche à accomplir.
find . -name '*.txt' | xargs grep -A1 'Foo' | grep -B1 'Bar'
Bien que je préfère utiliser la solution de Nathan pcregrep, voici une solution utilisant uniquement grep
grep -o -z -P 'Foo(.*)\n(.*)Bar' file
Explication des options:
-oimprimer uniquement la partie assortie. Nécessaire car l'inclusion de -zva imprimer tout le fichier (sauf s'il y a un \ 0 quelque part)-z Traitez l'entrée comme un ensemble de lignes, chacune se terminant par un octet zéro (le caractère ASCII NUL) au lieu d'une nouvelle ligne.-P syntaxe de regex perl EDIT: Cette version imprime des lignes appariées entières
grep -o -P -z '(.*)Foo(.*)\n(.*)Bar(.*)' file
-z. Quelques «(. *)» Avant et après l'expression entière lui feraient sortir toutes les lignes correspondantes. Pour l'instant, les sous-chaînes avant "Foo" et après "Bar" ne sont pas affichées.
Avec awk:
awk '/bar/ && prev != "" {print FILENAME ": " prev "\n" FILENAME ": " $0}
/foo/ {prev=$0; next}
{prev=""}' file1...
(remarque générale sur la limitation de awk: attention, si certains noms de fichiers peuvent contenir des caractères "=", vous devrez les passer comme ./filenameau lieu de filenameawk)