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.txt
Datei.
$ 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 N
die 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.