Como exibir certas linhas de um arquivo de texto no Linux?

85

Eu acho que todo mundo sabe que os utilitários de linha de cmd úteis Linux heade tail. headpermite imprimir as primeiras linhas X de um arquivo, tailfaz o mesmo, mas imprime o final do arquivo. Qual é um bom comando para imprimir no meio de um arquivo? algo como middle --start 10000000 --count 20(imprima as 10.000 '000ª até as 10.000 '010ª linhas).

Estou procurando algo que lide com arquivos grandes com eficiência. Eu tentei tail -n 10000000 | head 10e é terrivelmente lento.

Boaz
fonte
5
possível duplicação de serverfault.com/questions/101900/…
Kyle Brandt

Respostas:

111
sed -n '10000000,10000020p' filename

Você pode acelerar isso um pouco assim:

sed -n '10000000,10000020p; 10000021q' filename

Nesses comandos, a opção -nfaz sedcom que "suprima a impressão automática do espaço do padrão". O pcomando "imprime [s] o espaço padrão atual" e o qcomando "Encerra imediatamente [s] o script sed sem processar mais nenhuma entrada ..." As aspas são da sed manpágina .

A propósito, seu comando

tail -n 10000000 filename | head 10

inicia na décima milionésima linha do final do arquivo, enquanto o comando "intermediário" parece começar na décima milionésima parte do início, o que seria equivalente a:

head -n 10000010 filename | tail 10

O problema é que, para arquivos não classificados com linhas de comprimento variável, qualquer processo precisará passar pelo arquivo contando novas linhas. Não há como atalho isso.

Se, no entanto, o arquivo for classificado (um arquivo de log com registros de data e hora, por exemplo) ou tiver linhas de comprimento fixo, você poderá procurar no arquivo com base na posição de bytes. No exemplo do arquivo de log, você pode fazer uma pesquisa binária por um intervalo de vezes como o meu script Python aqui *. No caso do arquivo com tamanho fixo de registro, é realmente fácil. Você apenas procura linelength * linecountcaracteres no arquivo.

* Eu mantenho a intenção de postar mais uma atualização para esse script. Talvez eu consiga resolver isso um dia desses.

Dennis Williamson
fonte
Aqui está uma sedversão do Charles' middlefunção: middle() { local s=$1 c=$2; shift 2; sed -n "$s,$(($s + $c -1))p; $(($s + $c))q" "$@"; }. Ele manipulará vários argumentos de arquivo, nomes de arquivos com espaços, etc. Vários arquivos são processados ​​juntos como se tivessem sido criados da mesma maneira que sednormalmente (portanto, 1000 100 100 arquivo1 arquivo2 intermediário se estenderia do final do primeiro arquivo ao início do segundo se o primeiro tiver menos de 1100 linhas).
Dennis Williamson
A função no meu comentário anterior pode ser chamado com um parâmetro filename: middle startline count filenameou vários nomes de arquivos: middle startline count file1 file2 file3ou com redirecionamento: middle startline count < filenameou em um tubo: some_command | count` startline meio oucat file* | middle startline count
Dennis Williamson
O `no seu comando sed não deveria ser um '? Não consigo fazê-lo funcionar com o backtick, mas funciona bem com aspas simples.
Ian Hunter
@beanland: Sim, é um erro de digitação. Eu consertei isso. Obrigado.
Dennis Williamson
11
@ kev: adicionei algumas explicações à minha resposta.
Dennis Williamson
28

Eu descobri o seguinte uso de sed

sed -n '10000000,+20p'  filename

Espero que seja útil para alguém!

Dox
fonte
É bom saber que existe uma alternativa ao argumento da última linha proposto por Dennis: uma contagem da linha como segundo sed -nargumento, o que o torna bastante legível.
User3123159
Um exemplo de uso: extract_lines(){sed -n "$1,+$2p" <file>}que grava no stdout.
User3123159
4

Esta é minha primeira vez postando aqui! Enfim, este é fácil. Digamos que você queira extrair a linha 8872 do seu arquivo chamado file.txt. Aqui está como você faz isso:

cat -n arquivo.txt | grep '^ * 8872'

Agora, a questão é encontrar 20 linhas depois disso. Para conseguir isso, você faz

cat -n arquivo.txt | grep -A 20 '^ * 8872'

Para linhas ao redor ou antes, veja os sinalizadores -B e -C no manual grep.

Dennis
fonte
Embora isso seja tecnicamente correto e seja uma maneira interessante de fazer isso em um arquivo de tamanho razoável, estou curioso sobre sua eficácia ao trabalhar com arquivos do tamanho que o pôster está perguntando.
Jenny D
Várias linhas: cat -n file.txt | grep "^ \ s \ + (10 \ | 20 \ | 30) \ s \ +"
Jeffrey Knight
cat -n file.txt | grep '^ *1'produza todas as linhas que têm 1 no lado direito. Como gerar a linha 1 com esta técnica? Eu sei que posso dirigir -n 1 .... mas como usar grep?
Sean87
1

A resposta sedutora de Dennis é o caminho a percorrer. Mas usando apenas cabeça e cauda, ​​sob o bash:

middle () {head -n $ [$ 1 + $ 2] | cauda -n $ 2; }

Isso varre as primeiras linhas de $ 1 + $ 2 duas vezes, então é muito pior do que a resposta de Dennis. Mas você não precisa se lembrar de todas aquelas cartas sed para usá-lo ....

Charles Stewart
fonte
O uso $[...]foi preterido, pelo menos no Bash. Além disso, você está perdendo um parâmetro de arquivo.
Dennis Williamson
@ Dennis: Nenhum parâmetro ausente: você deve usar isso no stdin, conforme middle 10 10 < /var/log/auth.log.
Charles Stewart
1

Use o comando a seguir para obter o intervalo específico de linhas

awk 'NR < 1220974{next}1;NR==1513793{exit}' debug.log | tee -a test.log

Aqui, debug.log é o meu arquivo, que consiste em uma falta de linhas e eu costumava imprimir as linhas do número da linha 1220974 para 1513793 em um arquivo test.log. espero que seja útil para capturar o intervalo de linhas.

newbie13
fonte
A mesma resposta que serverfault.com/a/641252/140016 . Votado.
Deer Hunter
Não é a mesma resposta. Isso deve ser mais rápido para arquivos grandes, pois ele é interrompido após a impressão da última linha, em vez de continuar a digitalização no arquivo.
Phobic
0

Uma versão ruby ​​oneliner.

ruby -pe 'next unless $. > 10000000 && $. < 10000020' < filename.txt

Pode ser útil para alguém. As soluções com 'sed' fornecidas por Dennis e Dox são muito boas, mesmo porque parecem mais rápidas.

shardan
fonte
0

Você pode usar 'nl'.

nl filename | grep <line_num>
Ajay
fonte
0

Por exemplo, este awk imprimirá linhas entre 20 e 40

awk '{if ((NR> 20) && (NR <40)) imprime $ 0}' / etc / passwd

Hrvoje Špoljar
fonte
0

Se você conhece os números das linhas, diga que deseja obter as linhas 1, 3 e 5 de um arquivo, diga / etc / passwd:

perl -e 'while(<>){if(++$l~~[1,3,5]){print}}' < /etc/passwd
Dagelf
fonte
0

Perl é rei:

perl -ne 'print if ($. == 10000000 .. $. == 10000020)' filename
Peter V. Mørch
fonte