Imprime a linha antes da linha correspondente, linha de correspondência e enésima linha da linha correspondente

3

Eu quero imprimir a linha n antes da linha correspondente, da linha correspondente e da enésima linha da linha correspondente, em que "n" é maior que 2.

Aqui está um exemplo do meu arquivo de dados (os números de linha abaixo não fazem parte dos dados e apenas para identificação), O padrão que estou procurando é "blah", no arquivo example.txt . / em>

$ 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

E eu quero o resultado como:

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

Por favor, sugira qualquer um dos liners!

    
por Kumaran 01.10.2013 / 18:22

5 respostas

2

Aqui está um perl one-liner:

$ 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

Para alterar o número de linhas circunvizinhas, altere $n=3; para $n=N , em que N é o número desejado. Para alterar o padrão correspondente, altere if ($lines[$i]=~/blah/) para if ($lines[$i]=~/PATTERN/) .

Se os números são realmente parte do arquivo, você pode fazer algo assim:

$ 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
    
por 01.10.2013 / 18:42
4
awk -vn=3 '/blah/{print l[NR%n];print;p[NR+n]};(NR in p);{l[NR%n]=$0}'

Isso pressupõe que não há sobreposição. Se houver sobreposição, todas as linhas relevantes serão impressas, mas possivelmente várias vezes e não necessariamente na mesma ordem da entrada.

Para evitar isso, você pode escrevê-lo da seguinte forma:

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

Em uma entrada como:

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

O primeiro seria:

2
blah1
blah1
blah2
blah2
5
blah3
8
9

Enquanto o segundo seria impresso:

2
blah1
5
blah2
blah3
8
9
    
por 01.10.2013 / 19:49
2

Aqui está uma resposta semelhante à de @ terdon, mas só mantém as 2n + 1 linhas relevantes na memória:

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

E você executaria da seguinte forma: perl example.pl 3 blah example.txt

    
por 01.10.2013 / 20:55
1
egrep -B 2 blah example.txt

grep é a família:

1. grep 
2. egrep  # for regular expression
3. fgrep  # fast grep

A opção dada:

-B == Before 
-A == After 
-C == -B + -C

Claro, as opções acima são gnu extension você não as tem no Solaris.

    
por 01.10.2013 / 18:26
0

Não é muito eficiente. Agarre números de linha usando grep, imprima números de linha usando 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

resulta em

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

Ele provavelmente falharia se algum desses números acabasse ficando fora do intervalo.

    
por 02.10.2013 / 14:18