Como manter as últimas 50 linhas no logfile

19

Eu 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 de arquivo de teste e insira-o 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?

    
por dorinand 29.07.2016 / 17:02

6 respostas

26

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

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

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

Isso produzirá o que você está procurando,

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

Explicação:

  • $() é chamado de substituição de comando que executa tail -n 50 /home/pi/Documents/test
  • as aspas preservam as quebras de linha na saída.
  • > /home/pi/Documents/test redireciona a saída de echo "$(tail -n 50 /home/pi/Documents/test)" para o mesmo arquivo.
por 29.07.2016 / 17:21
8

Outra solução para o redirecionamento de arquivos que está limpando o arquivo primeiro é usar sponge do pacote moreutils assim :

tail -n 50 /home/pi/Documents/test | sponge /home/pi/Documents/test
    
por 30.07.2016 / 21:22
6

Isso ocorre porque o bash processa o redirecionamento com o > primeiro, excluindo o conteúdo do arquivo. Então 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 como esperado ao redirecionar para um arquivo diferente. Aqui está uma forma de escrever 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

Primeiro, grava as últimas 50 linhas em um arquivo temporário, que é movido usando mv para substituir o arquivo original.

Como 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, a cada vez que isso acontece, seu conteúdo seja sobrescrito.

    
por 29.07.2016 / 17:05
4

Em uma faixa ligeiramente diferente, você pode usar logrotate(8) para fazer backup dos arquivos de log regularmente para arquivos nomeados incrementalmente e depois excluir os antigos.

É assim que os principais arquivos de log do sistema são gerenciados para evitar que cresçam por muito tempo.

    
por 30.07.2016 / 16:40
3

Já que você viu o problema principal com o redirecionamento de shell, aqui está uma maneira alternativa de remover um arquivo para suas ú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 por (GNU) sed com o recurso -i "edição no local", que funciona sob as capas criando a saída em um arquivo temporário. O restante das linhas configuram as contas 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.
por 29.07.2016 / 18:15
3
printf '%s\n' '1,$-50d'   w | ed -s /home/pi/Documents/tes

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

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

Não há redirecionamentos envolvidos, portanto, o shell não pode sobrescrever o arquivo de saída antes que ele seja lido.

Além disso, diferentemente da maioria das formas de edição "no local" (que normalmente simulam a edição "no local" criando um arquivo temporário e renomeando-o sobre o original), ed edita o arquivo original ele mantém o mesmo inode (e proprietário, grupo e permissões - tempfile + mv irá sempre alterar o inode, e pode alterar os outros dependendo das circunstâncias).

    
por 30.07.2016 / 07:34