Como manter as últimas 50 linhas no arquivo de log

21

Tento manter as últimas 50 linhas no meu arquivo, onde salvo a temperatura a cada minuto. Eu usei este comando:

tail -n 50 /home/pi/Documents/test > /home/pi/Documents/test

Mas o resultado é um arquivo de teste vazio. Eu pensei, ele lista as últimas 50 linhas do arquivo de teste e o insere no arquivo de teste. Quando eu uso este comando:

tail -n 50 /home/pi/Documents/test > /home/pi/Documents/test2

está funcionando bem. Existem 50 linhas no arquivo test2.

Alguém pode me explicar onde está o problema?

dorinand
fonte
2
Algo como rrdtool pode ser mais apropriado para manter N registros (entre outras estatísticas) ao longo do tempo.
thrig 29/07
Veja também unix.stackexchange.com/a/147620/117549
Jeff Schaller
2
problema de truncamento clássico
haylem 29/07
Se você estiver usando python para gerar seus logs, deverá examinar o loggingmódulo #
Wayne Werner

Respostas:

29

O problema é que seu shell está configurando o pipeline de comandos antes de executar os comandos. Não é uma questão de "entrada e saída", é que o conteúdo do arquivo já desapareceu antes mesmo da execução da cauda. É algo como:

  1. O shell abre o >arquivo de saída para gravação, truncando-o
  2. O shell é configurado para que o descritor de arquivo 1 (para stdout) seja usado para essa saída
  3. O shell é executado tail.
  4. tailcorre, abre /home/pi/Documents/teste não encontra nada lá

Existem várias soluções, mas a chave é entender o problema, o que realmente está errado e o porquê.

Isso produzirá o que você está procurando,

echo "$(tail -n 50 /home/pi/Documents/test)" > /home/pi/Documents/test

Explicação:

  • $() é chamado substituição de comando que executa tail -n 50 /home/pi/Documents/test
  • as aspas preservam quebras de linha na saída.
  • > /home/pi/Documents/testredireciona a saída echo "$(tail -n 50 /home/pi/Documents/test)"para o mesmo arquivo.
Rahul
fonte
Obrigado, é funciona bem! Eu tenho mais uma pergunta. Você poderia explicar como o seu procedimento funciona passo a passo?
31516 dorinand
1
Mas por que, no seu caso, o bash não executa primeiro? Eu não entendo como o bash processa o comando. Alguém pode explicar?
Dorinand 29/07
1
O> está no comando echo, portanto, é executado quando o comando echo inicia a execução. Não pode começar a execução antes de ser gravado. A substituição de variável é o que escreve o comando. Ele executa o comando aninhado e cria o comando echo substituindo o valor
jobermark
Quando tentei usar o mesmo para o arquivo de log de 44gb usando 5000 linhas em vez de 50, recebo um errobash: xrealloc: cannot allocate 18446744071562067968 bytes
Carmageddon
8

Outra solução para o redirecionamento de arquivo que limpa primeiro o arquivo é usar a spongepartir do moreutilspacote assim:

tail -n 50 /home/pi/Documents/test | sponge /home/pi/Documents/test
iobender
fonte
6

Isso ocorre porque o bash processa o redirecionamento com o >primeiro, excluindo o conteúdo do arquivo. Em seguida, ele executa o comando. Se você usasse >>, as últimas 50 linhas seriam anexadas ao final do que está atualmente no arquivo. Nesse caso, você teria as mesmas 50 linhas repetidas duas vezes.

O comando funciona conforme o esperado ao redirecionar para um arquivo diferente. Aqui está uma maneira de gravar as últimas 50 linhas de um arquivo em um arquivo com o mesmo nome:

tail -50 /home/pi/Documents/test > /home/pi/Documents/test2 && mv /home/pi/Documents/test2 /home/pi/Documents/test

Isso primeiro grava as últimas 50 linhas em um arquivo temporário, que é movido usando-se mvpara substituir o arquivo original.

Conforme observado nos comentários, isso não funcionará se o arquivo ainda estiver aberto. Mover o arquivo também cria um novo inode e pode alterar a propriedade e as permissões. Uma maneira melhor de fazer isso usando um arquivo temporário seria:

tail -50 /home/pi/Documents/test > /home/pi/Documents/test2 ; cat /home/pi/Documents/test2 > /home/pi/Documents/test

O arquivo temporário também pode ser removido, embora cada vez que isso aconteça, seu conteúdo seja sobrescrito.

clk
fonte
obrigado. Poderia me explicar passo a passo o que o bash executa? Não consigo imaginar como isso funciona.
Dorinand 29/07
tail -50 /home/pi/Documents/test >/tmp/foo && cat /tmp/foo >/home/pi/Documents/test
steve
1
observe que isso não funcionará se o arquivo de log ainda estiver aberto pelo processo de log (que continuará registrando no arquivo excluído original). tempfile + move resulta em um novo inode (quebra de links físicos) e possivelmente em proprietários ou permissões diferentes. tail ... > temp ; cat temp > orig ; rm -f temptrabalho.
cas
4

Como você viu o principal problema do redirecionamento de shell, eis uma maneira alternativa de remover um arquivo das últimas 50 linhas:

file=/path/to/the/file
n=$(( $(wc -l < "$file") - 50 ))
[[ $n -gt 0 ]] && sed -i 1,${n}d "$file"

O trabalho árduo é feito pelo (GNU) sed com o -irecurso "edição no local", que funciona sob os bastidores criando a saída em um arquivo temporário. O restante das linhas configurou a matemática para a operação do sed, a saber:

  1. conte as linhas no arquivo ( wc) e subtraia 50; atribuir isso a n.
  2. se n for positivo, execute o comando sed para excluir as linhas 1 a n.
Jeff Schaller
fonte
4
printf '%s\n' '1,$-50d'   w | ed -s /home/pi/Documents/tes

printfé usado para canalizar comandos (um por linha) ed. Os edcomandos são:

  • 1,$-50d - excluir todas as últimas 50 linhas, exceto as últimas
  • w - escreve o arquivo modificado de volta ao disco

Como não há redirecionamentos envolvidos, o shell não pode sobrescrever o arquivo de saída antes de ser lido.

Além disso, ao contrário da maioria das formas de edição "no local" (que normalmente simula a edição "no local", criando um arquivo temporário e renomeá-lo sobre o original), edna verdade , edita o arquivo original - para manter o mesmo inode ( e proprietário, grupo e permissões - tempfile + mv sempre alterará o inode e poderá alterar os outros, dependendo das circunstâncias).

cas
fonte
4

Em uma faixa um pouco diferente, você pode usar logrotate(8)o backup regularmente dos arquivos de log em arquivos nomeados de forma incremental e excluir os antigos.

É assim que os principais arquivos de log do sistema são gerenciados para impedir que eles cresçam demais.

CSM
fonte