Comment conserver les 50 dernières lignes dans le fichier journal

22

J'essaye de garder les 50 dernières lignes dans mon fichier où j'économise la température toutes les minutes. J'ai utilisé cette commande:

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

Mais le résultat est un fichier de test vide. J'ai pensé, il listera les 50 dernières lignes du fichier de test et l'insérera dans le fichier de test. Lorsque j'utilise cette commande:

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

ça marche bien. Il y a 50 lignes dans le fichier test2.

Quelqu'un peut-il m'expliquer où est le problème?

dorinand
la source
2
Quelque chose comme rrdtool peut être plus approprié pour conserver N enregistrements (entre autres statistiques) au fil du temps.
thrig
Voir aussi unix.stackexchange.com/a/147620/117549
Jeff Schaller
2
problème de troncature classique
haylem
Si vous utilisez python pour générer vos journaux, vous devriez regarder dans le loggingmodule
Wayne Werner

Réponses:

30

Le problème est que votre shell configure le pipeline de commandes avant d'exécuter les commandes. Ce n'est pas une question "d'entrée et de sortie", c'est que le contenu du fichier a déjà disparu avant même que tail ne s'exécute. Cela va quelque chose comme:

  1. Le shell ouvre le >fichier de sortie pour l'écriture, le tronquant
  2. Le shell se configure pour que le descripteur de fichier 1 (pour stdout) soit utilisé pour cette sortie
  3. Le shell s'exécute tail.
  4. tailcourt, s'ouvre /home/pi/Documents/testet n'y trouve rien

Il existe différentes solutions, mais la clé est de comprendre le problème, ce qui ne va pas et pourquoi.

Cela produira ce que vous cherchez,

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

Explication:

  • $() est appelé substitution de commandes qui exécute tail -n 50 /home/pi/Documents/test
  • les guillemets préservent les sauts de ligne dans la sortie.
  • > /home/pi/Documents/testredirige la sortie de echo "$(tail -n 50 /home/pi/Documents/test)"vers le même fichier.
Rahul
la source
Merci ça marche bien! J'ai une autre question. Pourriez-vous expliquer comment votre procédure fonctionne pas à pas?
dorinand
1
Mais pourquoi dans votre cas bash ne joue pas en premier? Je ne comprends pas comment bash traite la commande. Quelqu'un peut-il expliquer?
dorinand
1
> Est sur la commande echo, il est donc exécuté lorsque la commande echo commence son exécution. Il ne peut pas commencer l'exécution avant d'être écrit. La substitution de variable est ce qui écrit la commande. Il exécute la commande imbriquée et crée la commande echo en remplaçant la valeur.
jobermark
Lorsque j'ai essayé d'utiliser le même pour le fichier journal de 44 Go en utilisant 5000 lignes au lieu de 50, j'obtiens une erreurbash: xrealloc: cannot allocate 18446744071562067968 bytes
Carmageddon
8

Une autre solution pour la redirection de fichiers effaçant le fichier en premier est d'utiliser spongele moreutilspack comme ceci:

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

En effet, bash traite la redirection avec la >première, en supprimant le contenu du fichier. Il exécute ensuite la commande. Si vous l'utilisiez >>, les 50 dernières lignes seraient ajoutées à la fin de ce qui se trouve actuellement dans le fichier. Dans ce cas, vous auriez les mêmes 50 lignes répétées deux fois.

La commande fonctionne comme prévu lors de la redirection vers un fichier différent. Voici une façon d'écrire les 50 dernières lignes d'un fichier dans un fichier du même nom:

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

Cela écrit d'abord les 50 dernières lignes dans un fichier temporaire, qui est ensuite déplacé en utilisant mvpour remplacer le fichier d'origine.

Comme indiqué dans les commentaires, cela ne fonctionnera pas si le fichier est toujours ouvert. Le déplacement du fichier crée également un nouvel inode et peut changer la propriété et les autorisations. Une meilleure façon de le faire en utilisant un fichier temporaire serait:

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

Le fichier temporaire peut également être supprimé, mais chaque fois que cela se produit, son contenu sera écrasé.

clk
la source
Merci. Pourriez-vous s'il vous plaît m'expliquer étape par étape ce que bash exécuter? Je ne peux pas imaginer comment cela fonctionne.
dorinand
tail -50 /home/pi/Documents/test >/tmp/foo && cat /tmp/foo >/home/pi/Documents/test
steve
1
notez que cela ne fonctionnera pas si le fichier journal est toujours ouvert par le processus de journalisation (qui continuera la journalisation dans le fichier supprimé d'origine). tempfile + move se traduit par un nouvel inode (brisant tous les liens durs), et éventuellement un propriétaire ou des perms différents. tail ... > temp ; cat temp > orig ; rm -f temptravaux.
cas
4

Puisque vous avez vu le principal problème avec la redirection du shell, voici une autre façon de tailler un fichier à ses 50 dernières lignes:

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

Le travail acharné est effectué par (GNU) sed avec la fonction -i"édition sur place", qui fonctionne sous les couvertures en créant la sortie dans un fichier temporaire. Le reste des lignes a établi les mathématiques pour le fonctionnement de sed, à savoir:

  1. compter les lignes du fichier ( wc), puis soustraire 50; assigner cela à n.
  2. si n est positif, exécutez la commande sed pour supprimer les lignes 1 à n.
Jeff Schaller
la source
4
printf '%s\n' '1,$-50d'   w | ed -s /home/pi/Documents/tes

printfest utilisé pour diriger les commandes (une par ligne) vers ed. Les edcommandes sont les suivantes:

  • 1,$-50d - supprimer toutes les lignes sauf les 50 dernières
  • w - réécrire le fichier modifié sur le disque

Aucune redirection n'est impliquée, le shell ne peut donc pas écraser le fichier de sortie avant sa lecture.

De plus, contrairement à la plupart des formes d'édition "sur place" (qui ne simulent généralement que l'édition "sur place" en créant un fichier temporaire puis en le renommant par rapport à l'original), edmodifie en fait le fichier d'origine - il conserve donc le même inode ( et propriétaire, groupe et autorisations - tempfile + mv changera toujours l'inode, et peut changer les autres selon les circonstances).

cas
la source
4

Sur une piste légèrement différente, vous pouvez utiliser logrotate(8)pour sauvegarder régulièrement les fichiers journaux dans des fichiers nommés de manière incrémentielle, puis supprimer les anciens.

C'est ainsi que les principaux fichiers journaux du système sont gérés pour éviter qu'ils ne s'allongent trop longtemps.

CSM
la source