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
Foo
et Bar
comprendrait toute la ligne.
Avec un sed
script:
#!/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' printf
afficher 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 space
et hold space
ICI .
grymoire.com a de très bonnes choses sur la shell
programmation.
h, n, H, x, p, q
signifie 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 grep
uniquement, vous pouvez construire le tuyau suivant:
grep -A1 'Foo' input_file | grep -B1 'Bar' | grep 'Foo'
Le premier grep
obtiendra toutes les lignes qui contiennent Foo
ainsi que la ligne après le match. Ensuite, nous obtenons des lignes qui contiennent Bar
ainsi 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 grep
la fonctionnalité orientée ligne, toute solution avec elle est susceptible d'être un «hack» et vous feriez probablement mieux d'utiliser quelque chose comme pcregrep
ce 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:
-o
imprimer uniquement la partie assortie. Nécessaire car l'inclusion de -z
va 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 ./filename
au lieu de filename
awk)