Je sais qu'avec grep, je peux utiliser les champs -A
et -B
pour extraire les lignes précédentes et suivantes d'une correspondance.
Cependant, ils tirent toutes les lignes entre la correspondance en fonction du nombre de lignes spécifié.
grep -r -i -B 5 -A 5 "match"
J'aimerais seulement recevoir le 5th ligne avant un match et le 5th ligne après le match en plus de la ligne correspondante et ne pas obtenir les lignes entre.
Y a-t-il un moyen de faire cela avec le grep
?
Si:
cat file
a
b
c
d
e
f match
g
h
i match
j
k
l
m
n
o
Ensuite:
awk '
{line[NR] = $0}
/match/ {matched[NR]}
END {
for (nr in matched)
for (n=nr-5; n<=nr+5; n+=5)
print line[n]
}
' file
a
f match
k
d
i match
n
Cela ne peut pas être fait avec seulement grep
. Si ed
est une option:
ed -s file << 'EOF'
g/match/-5p\
+5p\
+5p
EOF
Le script dit en gros: pour chaque correspondance de/match /, affichez la ligne 5 lignes avant cela, puis 5 lignes après cela, puis 5 lignes après.
awk '/match/{system("sed -n \"" NR-5 "p;" NR "p;" NR+5 "p\" " FILENAME)}' infile
Nous utilisons ici la fonction system(command)
de de awk pour appeler une commande externe sed
pour imprimer les lignes qui awk correspondent au modèle match
avec 5th lignes avant et après le match.
La syntaxe est simple, il vous suffit de placer la commande externe elle-même entre guillemets doubles ainsi que ses commutateurs et d'échapper aux éléments que vous souhaitez exactement transmettre à la commande; tout le reste lié aux options awk
proprement dites doit se trouver en dehors des guillemets. Donc, ci-dessous sed :
"sed -n \"" NR-5 "p;" NR "p;" NR+5 "p\" " FILENAME
se traduire par:
sed -n "NR-5p; NRp; NR+5p" FILENAME
NR
est le numéro de ligne correspondant au modèle match
et FILENAME
est le traitement en cours nom_fichier en passant par awk
.
Ceci est fondamentalement la solution de Glenn, mais implémentée avec Bash, Grep et sed.
grep -n match file |
while IFS=: read nr _; do
sed -ns "$((nr-5))p; $((nr))p; $((nr+5))p" file
done
Notez que les numéros de ligne inférieurs à 1 provoqueront une erreur sed et que les numéros de ligne supérieurs au nombre de lignes du fichier n'imprimeront rien.
Ceci est juste le strict minimum. Pour que cela fonctionne de manière récursive et traite les numéros de ligne ci-dessus, il faudrait faire quelques efforts.
en utilisant l'exemple de fichier texte de @ glenn et en utilisant Perl au lieu de awk:
$ Perl -n0E 'say /(.*\n)(?=(?:.*\n){4}(.*match.*\n)(?:.*\n){4}(.*\n))/g' ex
donnera les mêmes résultats, mais en courant plus vite:
a
f match
k
d
i match
n