web-dev-qa-db-fra.com

grep retourne les Nème et Mème lignes avant et après le match

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?

11
chollida

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
13
glenn jackman

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.

7
JoL
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.

6
αғsнιη

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.

6
wjandrea

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
2
JJoao