Como imprimirlinhas antes elinhas depoisa linha

Como imprimirlinhas antes elinhas depoisa linha

A pergunta feitaaquipede algumas linhas antes e depois da correspondência de padrões.

Mas aqui o objetivo é pegar um número de linha e buscar algumas linhas antes e depois de um arquivo

POR EXEMPLO:

seq 10
1
2
3
4
5
6
7
8
9
10

Se o número da linha for 6, ele deverá fornecer 4 números antes dessa linha e 3 números depois dessa linha. Aquilo é

2
3
4
5
6
7
8
9

Responder1

z=6   # focus line
x=4   # lines before
y=3   # lines after

start=$(( z - x ))
end=$(( z + y ))

Usando sed:

seq 10 | sed -n "$start,${end}p"
2
3
4
5
6
7
8
9

Isso simplesmente usa o pcomando print() sedcom um intervalo explícito de linhas para imprimir. As outras linhas são ignoradas usando -n.

Usando awk:

seq 10 | awk -v start="$start" -v end="$end" 'NR >= start { print } NR >= end { exit }'
2
3
4
5
6
7
8
9

Isto é semelhante à resposta de Stéphane Chazelas, mas implementada em awk; o script começa a gerar as linhas de entrada após ler starto número de linhas. No endnúmero de linhas, o script é encerrado.

Ambas as alternativas exibirão uma parte dos dados de entrada, começando nas xlinhas antes da linha ze terminando ynas linhas após a linha z.

Responder2

Com shells POSIX:

$ before=4 after=3 line=6
$ seq 10 | sed "$((line-before)),\$!d; $((line+after))q"
2
3
4
5
6
7
8
9

Traduz para:

  • delete qualquer linha, exceto ( !) do intervalo dolinha - antesaquele até o final ( $).
  • quito nolinha + depoisa linha

Dessa forma, nem nos preocupamos em ler além dolinha + depoisa linha.

Isso significa, entretanto, que o comando que alimenta seus dados sedserá abortado com um SIGPIPE se continuar enviando dados logo após o que pode ou não ser desejável.

Responder3

Apenas para completar:

$ l=60;seq 100 |head -n$((l+3)) |tail -n+$((l-4))
56
57
58
59
60
61
62
63

Rumores e vários benchmarks dizem que a combinação head + tail é muito mais rápida do que qualquer outra ferramenta:

$ a=1000000000
$ time seq $a |awk 'NR>=499998{print}NR >= 500004 { exit }' 
499998
499999
500000
500001
500002
500003

real    0m0.158s
user    0m0.152s
sys 0m0.004s

$ time seq $a |sed -n "499998,500003p"
499998
499999
500000
500001
500002
500003

real    1m30.249s
user    1m21.284s
sys 0m12.312s

$ time seq $a |sed "$((500000-2)),\$!d; $((500000+3))q"  #Stephan's Solution
499998
499999
500000
500001
500002
500003

real    0m0.052s
user    0m0.044s
sys 0m0.004s

$ time seq $a |head -n$((500000+3)) |tail -n+$((500000-2))
499998
499999
500000
500001
500002
500003

real    0m0.024s
user    0m0.024s
sys 0m0.004s

$ time seq $a |sed -n "499998,500003p;500004q"
499998
499999
500000
500001
500002
500003

real    0m0.056s
user    0m0.048s
sys 0m0.004s

Responder4

# define line range constants
before=4
  line=6
 after=3

# setup the sed commands s.t. pattern space holds $before number
# of lines before we hit the line number $line and $after after
s='$!N'
p=`seq -s "$s"   "$before"`
a=`seq -s "$s" 0 "$after"`

N=${p//[0-9]/;}
n=${a//[0-9]/;}

# main...
seq 10 |
sed -e "
   1{ $N }
   \$d;N
   $line!D
   $n;q
"

Outro método é sorver o arquivo e definir FScomo \npara que os campos (agora linhas) fiquem em @F. O que resta é cortá-lo em torno da 6ª linha e 4 elementos antes e 3 linhas depois:

perl -alF\\n -0777ne '$,=$\;print @F[6-4-1..6+3-1]' yourfile

Resultados

2
3
4
5
6
7
8
9

informação relacionada