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
...
grep
le 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 m
lignes avant la correspondance et n
lignes après la correspondance. Ou vous pouvez utiliser
grep text_to_search -C n file.extension
pour afficher un total de n
lignes entourant le texte trouvé (moitié avant, moitié après le match).
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
où 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
La commande sed
suivante 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 h
name__. Enfin, nous échangeons les espaces de maintien et de motif avec x
et imprimons cet espace de motifs avec p
name__. À ce stade, sed
continuera à traiter le fichier ligne par ligne à la recherche d'une autre correspondance.
Une référence pratique pour les commandes sed
peut être trouvée ici .
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