Drucken Sie die n-te Zeile vor der übereinstimmenden Zeile, die übereinstimmende Zeile und die n-te Zeile nach der übereinstimmenden Zeile

Drucken Sie die n-te Zeile vor der übereinstimmenden Zeile, die übereinstimmende Zeile und die n-te Zeile nach der übereinstimmenden Zeile

Ich möchte die n-te Zeile vor der übereinstimmenden Zeile, die übereinstimmende Zeile und die n-te Zeile ab der übereinstimmenden Zeile drucken, wobei „n“ größer als 2 ist.

Hier ist ein Beispiel meiner Datendatei (die Zeilennummern unten sind nicht Teil der Daten und dienen nur zur Identifizierung). Das Muster, nach dem ich suche, ist „blah“ in der example.txtDatei.

$ cat example.txt 
 1. a
 2. b
 3. c
 4. d
 5. blah
 6. e
 7. f
 8. g
 9. h
 10. blah
 11. i
 12. f
 13. g
 14. h

Und ich möchte die Ausgabe wie folgt:

 1. b
 2. blah
 3. g
 4. f
 5. blah
 6. g

Bitte schlagen Sie einen Einzeiler vor!

Antwort1

awk -vn=3 '/blah/{print l[NR%n];print;p[NR+n]};(NR in p);{l[NR%n]=$0}'

Dies setzt voraus, dass es keine Überlappungen gibt. Wenn es Überlappungen gibt, werden alle relevanten Zeilen gedruckt, aber möglicherweise mehrmals und nicht unbedingt in der gleichen Reihenfolge wie in der Eingabe.

Um das zu vermeiden, können Sie es stattdessen wie folgt schreiben:

awk -vn=3 '/blah/{p[NR-n]p[NR]p[NR+n]};(NR-n in p){print l[NR%n]}
  {l[NR%n]=$0};END{for(i=NR-n+1;i<=NR;i++)if (i in p) print l[i%n]}'

Bei einer Eingabe wie:

1
2
3
4
blah1
5
6
blah2
blah3
7
8
9
10

Das erste würde ergeben:

2
blah1
blah1
blah2
blah2
5
blah3
8
9

Während der zweite drucken würde:

2
blah1
5
blah2
blah3
8
9

Antwort2

Hier ist ein Perl-Einzeiler:

$ perl -ne '$n=3;push @lines,$_; END{for($i=0;$i<=$#lines;$i++){
  if ($lines[$i]=~/blah/){
    print $lines[$i-$n],$lines[$i],$lines[$i+$n]}}
 }' example.txt 
b
blah
g
f
blah
g

Um die Anzahl der umgebenden Zeilen zu ändern, wechseln Sie $n=3;zu $n=N, wobei Ndie gewünschte Zahl ist. Um das übereinstimmende Muster zu ändern, wechseln Sie if ($lines[$i]=~/blah/)zu if ($lines[$i]=~/PATTERN/).

Wenn die Zahlen tatsächlich Teil der Datei sind, können Sie Folgendes tun:

$ perl -ne '$n=3;push @lines,$_; END{for($i=0;$i<=$#lines;$i++){
      if ($lines[$i]=~/blah/){
        print $lines[$i-$n],$lines[$i],$lines[$i+$n]}}
     }' example.txt | perl -pne 's/\d+/$./'
1. b
2. blah
3. g
4. f
5. blah
6. g

Antwort3

Hier ist eine ähnliche Antwort wie die von @terdon, aber sie behält nur die 2n+1 relevanten Zeilen im Speicher:

my $n = shift;
my $pattern = shift;
my @lines = ("\n") x (2*$n+1);
while (<>) {
    shift @lines;
    push @lines, $_;
    if ($lines[$n] =~ m/$pattern/) {
        print $lines[0], $lines[$n], $lines[-1];
    }
}

Und Sie würden es so ausführen:perl example.pl 3 blah example.txt

Antwort4

Nicht besonders effizient. Zeilennummern mit grep erfassen, Zeilennummern mit sed drucken.

for n in `grep -n blah example.txt | sed -e s/:.*//`
do
    sed -n -e "$[$n-3]p" -e "$[$n]p" -e "$[$n+3]p" example.txt
done

führt zu

 2. b
 5. blah
 8. g
 7. f
 10. blah
 13. g

Wenn eine dieser Zahlen außerhalb des gültigen Bereichs liegen würde, würde der Vorgang wahrscheinlich scheitern.

verwandte Informationen