Qual é a melhor maneira de retirar um segmento de um arquivo de texto?

10

O que é uma boa maneira de extrair, digamos, linhas 20 -45 de um arquivo de texto enorme. Não interativamente, claro!

    
por Chris Huang-Leaver 15.09.2010 / 15:20

5 respostas

11

você pode tentar:

cat textfile | head -n 45 | tail -n 26

ou

cat textfile | awk "20 <= NR && NR <= 45" 

update:

Como o Mahomedalid apontou, cat não é necessário e um pouco redundante, mas cria um comando legível e limpo.

Se cat incomodar você, uma melhor solução seria:

<textfile awk "20 <= NR && NR <= 45"
    
por 15.09.2010 / 15:25
13

Ainda mais simples:

sed -n '20,45p;45q' < textfile

O sinalizador -n desativa a saída padrão. O "20,45" endereça as linhas 20 a 45, inclusive. O comando "p" imprime a linha atual. E o q sai depois de imprimir a linha.

    
por 15.09.2010 / 18:46
6

Esta não é uma resposta, mas não pode ser publicada como comentário.

Outra maneira (muito rápida) de fazer isso foi sugerida por mikeserv aqui :

{ head -n 19 >/dev/null; head -n 26; } <infile

Usando o mesmo arquivo de teste como aqui e o mesmo procedimento, aqui estão alguns benchmarks (extraindo linhas 1000020-1000045) :

mikeserv :

{ head -n 1000019 >/dev/null; head -n 26; } <iplist

real    0m0.059s

Stefan :

head iplist -n 1000045 | tail -n 26

real    0m0.054s

Estas são de longe as soluções mais rápidas e as diferenças são insignificantes (para uma única passagem) (tentei com intervalos diferentes: algumas linhas, milhões de linhas, etc.).

Fazer isso sem o pipe pode oferecer uma vantagem significativa, no entanto, para um aplicativo que precisa buscar mais de vários intervalos de linhas de maneira semelhante, como:

for  pass in 0 1 2 3 4 5 6 7 8 9
do   printf "pass#$pass:\t"
     head -n99 >&3; head -n1
done <<1000LINES 3>/dev/null
$(seq 1000)
1000LINES

... que imprime ...

pass#0: 100
pass#1: 200
pass#2: 300
pass#3: 400
pass#4: 500
pass#5: 600
pass#6: 700
pass#7: 800
pass#8: 900
pass#9: 1000

... e só lê o arquivo pela única vez.

As outras soluções sed / awk / perl lêem o arquivo inteiro e, como se trata de arquivos grandes, não são muito eficientes. Eu joguei algumas alternativas que exit ou q uit após a última linha no intervalo especificado:

Stefan :

awk "1000020 <= NR && NR <= 1000045" iplist

real    0m2.448s

vs.

awk "NR >= 1000020;NR==1000045{exit}" iplist

real    0m0.243s

dkagedal ( sed ):

sed -n 1000020,1000045p iplist

real    0m0.947s

vs.

sed '1,1000019d;1000045q' iplist

real    0m0.143s

Steven D :

perl -ne 'print if 1000020..1000045' iplist

real    0m2.041s

vs.

perl -ne 'print if $. >= 1000020; exit if $. >= 1000045;' iplist

real    0m0.369s
    
por 06.04.2015 / 21:16
3
ruby -ne 'print if 20 .. 45' file
    
por 16.09.2010 / 06:33
2

Como sed e awk já foram usados, aqui está uma solução de perl:

perl -nle "print if ($. > 19 && $. < 46)" < textfile

Ou, como apontado nos comentários:

perl -ne 'print if 20..45' textfile
    
por 15.09.2010 / 21:46