Comment puis-je grep un répertoire basé sur le contenu de deux lignes successives?

11

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"?

Nathan Long
la source
Le problème est maintenant totalement différent de l'original: / Peut-être préférable de revenir aux anciennes versions et d'en poster une autre? De plus, la nouvelle question n'est pas claire pour moi.
Gilles Quenot
@sputnick - comment cela? J'ai spécifié un répertoire lors de la première publication de la question; Je ne l'ai mis en gras que parce que les gens ne s'en rendaient pas compte.
Nathan Long
Peu importe, cela fonctionnera, je modifierai mon POST en conséquence.
Gilles Quenot

Réponses:

7

@ 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' .
Nathan Long
la source
6

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
Marguerite
la source
3
Le PO n'a pas dit cela Fooet Barcomprendrait toute la ligne.
tojrobinson
6

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.

Gilles Quenot
la source
Que h, n, H, x, p, qsignifie Très intéressant.
Yamaneko
Voir mes commentaires. Plus d'infos sur 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
Gilles Quenot
POST adapté pour travailler sur un annuaire
Gilles Quenot
4

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.

tojrobinson
la source
Agréable. J'ai demandé un répertoire cependant; cela semble fonctionner:find . -name '*.txt' | xargs grep -A1 'Foo' | grep -B1 'Bar'
Nathan Long
Cela listera également les occurrences avec "Foo" et "Bar" sur la même ligne.
manatwork
@manatwork: Les lignes qui contiennent "Foo" et "Bar" sont des "lignes qui contiennent 'Foo'", c'est ce qui a été demandé.
tojrobinson
1
@tojrobinson, qu'en est-il de la partie «mais obtenez uniquement des correspondances lorsque la ligne suivante contient également« Bar »»? pastebin.com/Yj8aeCEA
manatwork
3

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
bbaja42
la source
1
Truc cool quoi -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.
manatwork
1

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)

Stéphane Chazelas
la source