Qual é uma boa maneira de extrair, digamos, as linhas 20 a 45 de um grande arquivo de texto. Não interativamente, é claro!
Qual é uma boa maneira de extrair, digamos, as linhas 20 a 45 de um grande arquivo de texto. Não interativamente, é claro!
Respostas:
você poderia tentar:
cat textfile | head -n 45 | tail -n 26
ou
cat textfile | awk "20 <= NR && NR <= 45"
atualizar:
Como Mahomedalid apontou, cat
não é necessário e um pouco redundante, mas contribui para um comando limpo e legível.
Se cat
o incomoda, uma solução melhor seria:
<textfile awk "20 <= NR && NR <= 45"
,
operador de intervalo do awk .
Ainda mais simples:
sed -n '20,45p;45q' < textfile
O sinalizador -n desativa a saída padrão. O "20,45" aborda as linhas 20 a 45, inclusive. O comando "p" imprime a linha atual. E oq sai depois de imprimir a linha.
q
comando (tudo a partir de ;
) melhorou o desempenho para mim ao extrair uma única linha 26995107 de um arquivo de 27169334 linhas.
Esta não é uma resposta, mas não pode publicá-la como um 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 aqui e o mesmo procedimento, aqui estão alguns parâmetros de referência (extração das 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
Essas são, de longe, as soluções mais rápidas e as diferenças são insignificantes (para uma única passagem) (tentei com diferentes faixas: algumas linhas, milhões de linhas, etc.).
Fazer isso sem o cano pode oferecer uma vantagem significativa, no entanto, para um aplicativo que precisa procurar em vários intervalos de linhas da mesma maneira, 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 lê o arquivo apenas uma vez.
As outras sed
/ awk
/ perl
solutions leem o arquivo inteiro e, como se trata de arquivos enormes, eles não são muito eficientes. 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
awk NR==1000020,NR==1000045 textfile
em seu sistema.
ruby -ne 'print if 20 .. 45' file
python -c 'import fileinput, sys; [sys.stdout.write(line) for nr, line in enumerate(fileinput.input()) if 19 <= nr <= 44]'
também? :-P Isso é algo que Ruby, modelado após o Perl, inspirado no awk / sed, pode fazer facilmente.
Como sed e awk já foram usados, aqui está uma solução perl:
perl -nle "print if ($. > 19 && $. < 46)" < textfile
Ou, como apontado nos comentários:
perl -ne 'print if 20..45' textfile
perl -ne'print if 20..45' textfile
awk NR==20,NR==45 textfile
funciona também e lê com facilidade.