Вывести n-ную строку перед совпавшей строкой, совпадающую строку и n-ную строку от совпавшей строки

Вывести n-ную строку перед совпавшей строкой, совпадающую строку и n-ную строку от совпавшей строки

Я хочу напечатать n-ную строку перед совпавшей строкой, совпадающую строку и n-ную строку от совпавшей строки, где «n» больше 2.

Вот пример моего файла данных (номера строк ниже не являются частью данных и предназначены только для идентификации). Шаблон, который я ищу, — это «blah» в файле example.txt.

$ 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

И я хочу получить вывод как:

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

Пожалуйста, предложите какой-нибудь один вариант!

решение1

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

Это предполагает, что нет перекрытия. Если перекрытие есть, все соответствующие строки будут напечатаны, но, возможно, несколько раз и не обязательно в том же порядке, что и во входных данных.

Чтобы избежать этого, можно написать так:

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]}'

На входе типа:

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

Первый вариант даст:

2
blah1
blah1
blah2
blah2
5
blah3
8
9

В то время как второй вариант выведет:

2
blah1
5
blah2
blah3
8
9

решение2

Вот однострочный код на Perl:

$ 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

Чтобы изменить количество окружающих линий, измените $n=3;на $n=N, где Nесть желаемое число. Чтобы изменить сопоставленный шаблон, измените if ($lines[$i]=~/blah/)на if ($lines[$i]=~/PATTERN/).

Если числа действительно являются частью файла, вы можете сделать что-то вроде этого:

$ 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

решение3

Вот ответ, похожий на ответ @terdon, но он сохраняет в памяти только 2n+1 соответствующих строк:

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];
    }
}

И вы бы запустили это так:perl example.pl 3 blah example.txt

решение4

Не очень эффективно. Собирайте номера строк с помощью grep, печатайте номера строк с помощью sed.

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

результаты в

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

Вероятно, система потерпит неудачу, если какое-либо из этих чисел окажется за пределами допустимого диапазона.

Связанный контент