Digamos que você tenha um arquivo txt, qual é o comando para visualizar as 10 linhas superiores e as 10 linhas inferiores do arquivo simultaneamente?
ou seja, se o arquivo tiver 200 linhas, visualize as linhas 1-10 e 190-200 de uma só vez.
Digamos que você tenha um arquivo txt, qual é o comando para visualizar as 10 linhas superiores e as 10 linhas inferiores do arquivo simultaneamente?
ou seja, se o arquivo tiver 200 linhas, visualize as linhas 1-10 e 190-200 de uma só vez.
Respostas:
Você pode simplesmente:
(head; tail) < file.txt
E se você precisar usar tubos por algum motivo, faça o seguinte:
cat file.txt | (head; tail)
Nota: imprimirá linhas duplicadas se o número de linhas no arquivo.txt for menor que as linhas padrão da cabeça + linhas padrão da cauda.
head
consumiu as 10 primeiras linhas do arquivo. (Compare isso com head < file.txt; tail < file.txt
um arquivo com menos de 20 linhas). Apenas um ponto muito menor a ser lembrado. (Mas ainda +1.)
head
apenas exiba as 10 primeiras linhas de sua entrada, não há garantia de que ela não tenha consumido mais para encontrar a 10ª linha final, deixando menos da entrada para less
exibição.
seq 100 | (head; tail)
me dá apenas os primeiros 10 números. Somente em um tamanho de entrada muito maior (como seq 2000
) a cauda recebe alguma entrada.
Para um fluxo puro (por exemplo, saída de um comando), você pode usar 'tee' para bifurcar o fluxo e enviar um fluxo para a cabeça e outro para a cauda. Isso requer o uso do recurso '> (list)' do bash (+ / dev / fd / N):
( COMMAND | tee /dev/fd/3 | head ) 3> >( tail )
ou usando / dev / fd / N (ou / dev / stderr) mais subshells com redirecionamento complicado:
( ( seq 1 100 | tee /dev/fd/2 | head 1>&3 ) 2>&1 | tail ) 3>&1
( ( seq 1 100 | tee /dev/stderr | head 1>&3 ) 2>&1 | tail ) 3>&1
(Nenhum deles funcionará em csh ou tcsh.)
Para algo com um pouco de controle melhor, você pode usar este comando perl:
COMMAND | perl -e 'my $size = 10; my @buf = (); while (<>) { print if $. <= $size; push(@buf, $_); if ( @buf > $size ) { shift(@buf); } } print "------\n"; print @buf;'
COMMAND | { tee >(head >&2) | tail; } |& other_commands
cat >/dev/null
corrige:COMMAND | { tee >(head >&2; cat >/dev/null) | tail; } |& other_commands
head
e tail
comandos: \ ...
head -10 file.txt; tail -10 file.txt
Fora isso, você precisará escrever seu próprio programa / script.
cat
e head
outail
canalizei, bom saber que posso usá-los individualmente!
{ head file; tail file; } | prog
(espaçamento dentro das chaves, e o ponto e vírgula final são necessárias)
Baseado no comentário de JF Sebastian :
cat file | { tee >(head >&3; cat >/dev/null) | tail; } 3>&1
Dessa forma, você pode processar a primeira linha e o restante de maneira diferente em um pipe, o que é útil para trabalhar com dados CSV:
{ echo N; seq 3;} | { tee >(head -n1 | sed 's/$/*2/' >&3; cat >/dev/null) | tail -n+2 | awk '{print $1*2}'; } 3>&1
N * 2 2 4 6
o problema aqui é que os programas orientados a fluxo não sabem o tamanho do arquivo com antecedência (porque pode não haver um, se for um fluxo real).
ferramentas como tail
buffer das últimas n linhas vistas, aguarde o final do fluxo e depois imprima.
se você quiser fazer isso em um único comando (e fazê-lo funcionar com qualquer deslocamento, e não repetir as linhas se elas se sobreporem), será necessário emular esse comportamento que mencionei.
tente este awk:
awk -v offset=10 '{ if (NR <= offset) print; else { a[NR] = $0; delete a[NR-offset] } } END { for (i=NR-offset+1; i<=NR; i++) print a[i] }' yourfile
a.out | awk -v ...
Demorou muito tempo para terminar com esta solução, que parece ser a única que abrangeu todos os casos de uso (até agora):
command | tee full.log | stdbuf -i0 -o0 -e0 awk -v offset=${MAX_LINES:-200} \
'{
if (NR <= offset) print;
else {
a[NR] = $0;
delete a[NR-offset];
printf "." > "/dev/stderr"
}
}
END {
print "" > "/dev/stderr";
for(i=NR-offset+1 > offset ? NR-offset+1: offset+1 ;i<=NR;i++)
{ print a[i]}
}'
Lista de recursos:
Eu tenho procurado por esta solução por um tempo. Tentei eu mesmo com o sed, mas o problema de não saber o tamanho do arquivo / fluxo de antemão era insuperável. De todas as opções disponíveis acima, eu gosto da solução awk de Camille Goudeseune. Ele observou que sua solução deixou linhas em branco extras na saída com um conjunto de dados suficientemente pequeno. Aqui, forneço uma modificação de sua solução que remove as linhas extras.
headtail() { awk -v offset="$1" '{ if (NR <= offset) print; else { a[NR] = $0; delete a[NR-offset] } } END { a_count=0; for (i in a) {a_count++}; for (i=NR-a_count+1; i<=NR; i++) print a[i] }' ; }
Bem, você sempre pode amarrá-los juntos. Assim
head fiename_foo && tail filename_foo
. Se isso não for suficiente, você poderá escrever uma função bash em seu arquivo .profile ou em qualquer arquivo de logon usado:
head_and_tail() {
head $1 && tail $1
}
E, mais tarde, invocá-lo a partir do seu shell prompt: head_and_tail filename_foo
.
Primeiras 10 linhas de file.ext, depois as últimas 10 linhas:
cat file.ext | head -10 && cat file.ext | tail -10
As últimas 10 linhas do arquivo e as 10 primeiras:
cat file.ext | tail -10 && cat file.ext | head -10
Você também pode canalizar a saída para outro local:
(cat file.ext | head -10 && cat file.ext | tail -10 ) | your_program
tail
e / head
ou para uma função usando o alias.
Eu escrevi um aplicativo python simples para fazer isso: https://gist.github.com/garyvdm/9970522
Ele lida com tubos (fluxos) e também com arquivos.
Para manipular pipes (fluxos) e arquivos, adicione-o ao arquivo .bashrc ou .profile:
headtail() { awk -v offset="$1" '{ if (NR <= offset) print; else { a[NR] = $0; delete a[NR-offset] } } END { for (i=NR-offset+1; i<=NR; i++) print a[i] }' ; }
Então você não pode apenas
headtail 10 < file.txt
mas também
a.out | headtail 10
(Isso ainda acrescenta linhas em branco espúrias quando 10 excede o comprimento da entrada, diferente da antiga simples a.out | (head; tail)
. Obrigado, respondedores anteriores.)
Nota:, headtail 10
não headtail -10
.
Com base no que @Samus_ explicou aqui sobre como o comando de @Aleksandra Zalcman funciona, essa variação é útil quando você não consegue identificar rapidamente onde a cauda começa sem contar as linhas.
{ head; echo "####################\n...\n####################"; tail; } < file.txt
Ou, se você começar a trabalhar com algo diferente de 20 linhas, uma contagem de linhas pode até ajudar.
{ head -n 18; tail -n 14; } < file.txt | cat -n
Para imprimir as 10 e as 10 primeiras linhas de um arquivo, tente o seguinte:
cat <(head -n10 file.txt) <(tail -n10 file.txt) | less
sed -n "1,10p; $(( $(wc -l ${aFile} | grep -oE "^[[:digit:]]+")-9 )),\$p" "${aFile}"
NOTA : A variável aFile contém o caminho completo do arquivo .
Eu diria que, dependendo do tamanho do arquivo, a leitura ativa de seu conteúdo pode não ser desejável. Nessa circunstância, acho que alguns scripts simples de shell devem ser suficientes.
Aqui está como eu lidei com isso recentemente para vários arquivos CSV muito grandes que eu estava analisando:
$ for file in *.csv; do echo "### ${file}" && head ${file} && echo ... && tail ${file} && echo; done
Isso imprime as primeiras 10 linhas e as últimas 10 linhas de cada arquivo, além de imprimir o nome do arquivo e algumas reticências antes e depois.
Para um único arquivo grande, você pode simplesmente executar o seguinte para o mesmo efeito:
$ head somefile.csv && echo ... && tail somefile.csv
Consome stdin, mas simples e funciona para 99% dos casos de uso
#!/usr/bin/env bash
COUNT=${1:-10}
IT=$(cat /dev/stdin)
echo "$IT" | head -n$COUNT
echo "..."
echo "$IT" | tail -n$COUNT
$ seq 100 | head_and_tail 4
1
2
3
4
...
97
98
99
100