web-dev-qa-db-fra.com

Imprimer ligne X lignes avant / après ligne trouvée

Je veux rechercher une chaîne particulière dans un certain fichier. Si j'ai trouvé la chaîne, je veux aussi imprimer la ligne X lignes avant (ou après) cette ligne.

Est-ce que ceci peut avec grep ou awk, ou ai-je besoin d'une combinaison?

Je voudrais avoir quelque chose comme this , mais pas avec toutes les lignes de fuite avant/après un hit, seulement le Xème.

Par exemple, si mon entrée ressemble à:

line1 with a pattern
line2
line3
line4 with a pattern
line5
line6
line7 with a pattern
...

Je par exemple voulez rechercher le mot "modèle" et afficher cette ligne + la ligne qui se trouve 2 lignes après cela, mais pas la ligne qui suit directement la ligne avec le modèle. La sortie souhaitée est donc:

line1 with a pattern
line3
line4 with a pattern
line6
line7 with a pattern
...
7
MaVe

greple fera pour vous, avec les options -A (after) et -B (before), et -C (context). Un exemple que j'utilise souvent est:

   Sudo lspci -vnn | grep -i net -A 12

car cette commande affichera 12 lignes après le match, ce qui inclut le pilote utilisé pour contrôler la carte réseau (-i net). En général, la commande est la suivante:

  grep text_to_search -A n -B m file.extension

qui affichera mlignes avant la correspondance et nlignes après la correspondance. Ou vous pouvez utiliser

 grep text_to_search -C n file.extension

pour afficher un total de nlignes entourant le texte trouvé (moitié avant, moitié après le match).

14
MariusMatutiae

Après avoir examiné un peu cette question, je pense devoir réviser mon commentaire sur la faisabilité en utilisant des outils standard GNU sans script. Cela peut être très difficile à faire à cause des cas particuliers.

Si cela ne vous dérange pas d'utiliser awk, je pourrais vous proposer la solution suivante. Ce script context.awk est toujours assez concis:

{
    lines[NR] = $0
    if (dump[NR]) {
        print $0;
    if ($0 ~ Pattern) {
        if (NR-Delta in lines) {
            print "---"
            print lines[NR-Delta]
        }
        dump[NR+Delta] = 1
        print $0;
    }
    if (NR-Delta in lines) 
        delete lines [NR-Delta];
}

Il faut l'appeler comme suit:

awk -v Delta=X -v Pattern=PATTERN -f context.awk sample.txt

X est la "distance de contexte" souhaitée et PATTERN le modèle de recherche. Le script tente de séparer plusieurs contextes de modèle dans le fichier en imprimant des lignes avec --- entre les deux. Ainsi, par exemple, le sample.txt suivant

line1
line2
line3 XXX
line4
line5
line6
line7 XXX
line8
line9
line10 XXX
line11

en utilisant cet appel

awk -v Delta=3 -v Pattern=XXX -f context.awk sample.txt

donnera la sortie suivante

line3 XXX
line6
---
line4
line7 XXX
line10 XXX
---
line7 XXX
line10 XXX
3
Marcus Rickert

La commande sedsuivante semble fonctionner dans le cas que vous décrivez et que je reformulerais de la manière suivante: affiche la ligne correspondant à mon modèle, n’imprime pas la ligne immédiatement suivante, affiche également la ligne deux lignes après mon modèle, puis continue à rechercher des correspondances de modèle.

sed -n -e '/with a pattern/ {h;n;n;H;x;p}' file

Cela semble un peu moche, mais en ajoutant des commandes n(ignorer la ligne, passez à la suivante) et H(conserver cette ligne en l'ajoutant au tampon de retenue), vous pouvez créer une relation arbitraire entre conserver et ignorer à la suite d'une correspondance de modèle.

Notez qu'après la correspondance, nous copions initialement la ligne correspondante dans la mémoire tampon avec la commande hname__. Enfin, nous échangeons les espaces de maintien et de motif avec xet imprimons cet espace de motifs avec pname__. À ce stade, sedcontinuera à traiter le fichier ligne par ligne à la recherche d'une autre correspondance.

Une référence pratique pour les commandes sedpeut être trouvée ici .

1
deaks

grep -A2 <pattern> file | grep -B1 <pattern> | grep -v "\-\-" fonctionne pour moi:

user@box /tmp $ grep -A2 "with a pattern" test.txt | grep -B1 "with a pattern" | grep -v "\-\-"
line1 with a pattern
line3
line4 with a pattern
line6
line7 with a pattern

user@box /tmp $ cat test.txt 
line1 with a pattern
line2
line3
line4 with a pattern
line5
line6
line7 with a pattern
0
CHK