Quero imprimir a enésima linha antes da linha correspondente, a linha correspondente e a enésima linha da linha correspondente, onde "n" é maior que 2.
Aqui está um exemplo do meu arquivo de dados (os números das linhas abaixo não fazem parte dos dados e apenas para identificação). O padrão que estou procurando é "blá", no example.txt
arquivo.
$ 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 a saída como:
1. b
2. blah
3. g
4. f
5. blah
6. g
Por favor, sugira qualquer forro!
Responder1
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 haja 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ê poderia escrevê-lo como:
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 daria:
2
blah1
blah1
blah2
blah2
5
blah3
8
9
Enquanto o segundo seria impresso:
2
blah1
5
blah2
blah3
8
9
Responder2
Aqui está uma linha 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
Para alterar o número de linhas circundantes, mude $n=3;
para $n=N
onde N
está o número desejado. Para alterar o padrão correspondente, mude if ($lines[$i]=~/blah/)
para if ($lines[$i]=~/PATTERN/)
.
Se os números realmente fizerem parte do arquivo, você poderá 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
Responder3
Aqui está uma resposta semelhante à de @terdon, mas mantém apenas 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 assim:perl example.pl 3 blah example.txt
Responder4
Não é muito eficiente. Pegue os números das linhas usando grep e imprima os números das linhas 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
Provavelmente falharia se algum desses números ficasse fora do intervalo.