Eu tenho um arquivo com muitas linhas e cada linha tem um carimbo de data / hora no início, como
[Thread-3] (21/09/12 06:17:38:672) logged message from code.....
Portanto, frequentemente verifico duas coisas desse arquivo de log.
- As primeiras linhas, que possuem as condições globais e a hora de início, também são fornecidas.
- Últimas linhas, que tem o status de saída com outras informações.
Existe algum comando simples e rápido que me permita exibir apenas as primeiras e últimas linhas de um arquivo?
head and tail
funciona para você?N
sed(1)
especialista, mas existem maneiras de guardar coisas para uso posterior. Talvez valha a pena olhar lá dentro. OTOH, eu provavelmente criaria um script Perl (ou o que seja) para fazê-lo se usado com frequência, pois estou mais familiarizado com isso.Respostas:
Você pode usar
sed
ouawk
fazer isso com um comando. No entanto, você perderá a velocidade, causarásed
eawk
precisará executar todo o arquivo de qualquer maneira. Do ponto de vista da velocidade, é muito melhor criar uma função ou sempre a combinação detail
+head
. Isso tem a desvantagem de não funcionar se a entrada for um canal, no entanto, você pode usar a substituição de processo, caso o shell o suporte (veja o exemplo abaixo).e apenas iniciá-lo como
para prosseguir com a substituição do processo (somente bash, zsh, ksh como shells):
ps. você pode até adicionar um
grep
para verificar se suas "condições globais" existem.fonte
-n 10
é o padrão, não?-n 10
não é necessário aqui.O @rush tem razão em usar head + tail, sendo mais eficiente para arquivos grandes, mas para arquivos pequenos (<20 linhas), algumas linhas podem ser exibidas duas vezes.
seria igualmente eficiente, mas não teria o problema acima.
fonte
{head; tail;} < file
funciona no zsh, mas falha no sh.{ head; tail;} < file
sempre funciona. Desculpe pelo barulho.head
, não com o shell. O POSIX precisahead
deixar o cursor no arquivo além dessas 10 linhas para arquivos regulares. Pode surgir um problema parahead
implementações não POSIX (versões muito antigas do GNU head não eram conformes nesse caso, mas estamos falando de décadas) ou se o arquivo não for procurável (como pipe ou soquete nomeado, mas o outra solução teria o mesmo problema).sudo sh -c '{ head; tail;} < /path/to/file'
A
{ head; tail; }
solução não funcionaria em pipes (ou soquetes ou em outros arquivos não procuráveis) porquehead
poderia consumir muitos dados conforme lidos por blocos e não pode procurar novamente em um pipe, potencialmente deixando o cursor dentro do arquivo além do quetail
se entende selecionar.Portanto, você pode usar uma ferramenta que lê um caractere de cada vez como o do shell
read
(aqui, usando uma função que usa o número de linhas principais e finais como argumentos).ou implementar
tail
no awk, por exemplo, como:Com
sed
:(embora tenha em atenção que algumas
sed
implementações têm uma limitação baixa no tamanho do espaço do padrão, isso falharia com grandes valores do número de linhas finais).fonte
Usando a
bash
substituição do processo, você pode fazer o seguinte:Observe que não é garantido que as linhas estejam em ordem, embora, para arquivos com mais de 8kB, provavelmente estejam. Esse ponto de corte de 8 kB é o tamanho típico do buffer de leitura e está relacionado ao motivo pelo qual
| {head; tail;}
não funciona em arquivos pequenos.A
cat >/dev/null
é necessária para manter ohead
gasoduto vivo. Caso contrário,tee
sairá mais cedo e, enquanto você obtiver saídatail
, será de algum lugar no meio da entrada, e não no final.Finalmente, por que, em
>/dev/null
vez de, digamos, mudartail
para outro|
? No seguinte caso:head
O stdout é inserido no pipe,tail
e não no console, o que não é o que queremos.fonte
tail
precisa trabalhar mais, mas espero (e vejo) que falhe cerca da metade do tempo para entradas curtas.tee >(head) >(tail)
pelos mesmos motivos (>(...)
que, a propósito, é um recurso ksh agora suportado pelo zsh e pelo bash) também usam pipes. Você poderia fazer isso,... | (trap '' PIPE; tee >(head) >(tail) > /dev/null)
mas ainda verá algumas mensagens de erro de tubos quebradostee
.tail
está sendo morto pelo SIGPIPE, nãotee
etail
não está gravando em um pipe. Então deve ser de umkill()
, certo? E isso só acontece quando estou usando a|
sintaxe.strace
diz quetee
não está ligandokill()
... então talvezbash
?seq 100000 | tee >(head -n1) >(tail -n1) > /dev/null
Usando
ed
(que irá ler o arquivo inteiro na RAM):fonte
ed -s file <<< $'11,$-10d\n,p\nq\n'
A primeira solução de Stephane em uma função para que você possa usar argumentos (funciona em qualquer shell semelhante a Bourne ou POSIX):
Agora você pode fazer isso:
É claro que isso pressupõe que você esteja visualizando apenas um arquivo e que a solução de Stephane funcione (de maneira confiável) apenas em arquivos regulares (que podem ser procurados).
fonte
Com a opção
-u
(--unbuffered
) do GNUsed
, você pode usarsed -u 2q
como uma alternativa sem buffer parahead -n2
:(head -n2;tail -n2)
falha quando as últimas linhas fazem parte do bloco da entrada que é consumida porhead
:fonte
Encontrei algo assim hoje em dia, onde eu precisava apenas da última linha e algumas linhas da frente de um fluxo e criei o seguinte.
Eu li isso como: inicialize o espaço de espera com o conteúdo da primeira linha, acrescente as linhas 2-3 no espaço de espera, no EOF anexe a última linha ao espaço de espera, troque o espaço de espera e padrão e imprima o padrão espaço.
Talvez alguém com mais
sed
-fu do que eu posso imaginar como generalizar isso para imprimir as últimas poucas linhas do fluxo indicados nesta questão, mas eu não precisar e não poderia encontrar uma maneira fácil de fazer contas com base no$
endereço nosed
ou talvez por gerir o espaço hold para que apenas as últimas linhas estão em quandoEOF
é atingido.fonte
Você pode experimentar o Perl, se o tiver instalado:
Isso funcionará para a maioria dos arquivos, mas lê o arquivo inteiro na memória antes de processá-lo. Se você não estiver familiarizado com fatias Perl, "0" entre colchetes significa "pegue a primeira linha" e "-3 ...-1" significa "pegue as últimas três linhas". Você pode adaptar os dois de acordo com suas necessidades. Se você precisar processar arquivos muito grandes (o que é "grande" pode depender da sua RAM e talvez trocar tamanhos), convém:
pode ser um pouco mais lento, porque faz uma fatia a cada iteração, mas é independente do tamanho do arquivo.
Ambos os comandos devem funcionar em pipes e com arquivos regulares.
fonte