Jak zachować ostatnie 50 wierszy w pliku dziennika

22

Staram się przechowywać ostatnie 50 wierszy w moim pliku, w którym oszczędzam temperaturę co minutę. Użyłem tego polecenia:

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

Ale wynikiem jest pusty plik testowy. Myślałem, że wyświetli ostatnie 50 wierszy pliku testowego i wstawi go do pliku testowego. Kiedy używam tego polecenia:

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

działa dobrze. Plik test2 zawiera 50 wierszy.

Czy ktoś może mi wyjaśnić, gdzie jest problem?

dorinand
źródło
2
Coś takiego jak rrdtool może być bardziej odpowiednie do przechowywania N rekordów (między innymi statystykami) w czasie.
thrig
Zobacz także unix.stackexchange.com/a/147620/117549
Jeff Schaller
2
Klasyczny problem obcięcia
haylem
Jeśli używasz Pythona do generowania dzienników, powinieneś zajrzeć do loggingmodułu
Wayne Werner

Odpowiedzi:

30

Problem polega na tym, że powłoka konfiguruje potok poleceń przed uruchomieniem poleceń. Nie chodzi o „wejście i wyjście”, ale o to, że zawartość pliku zniknęła, zanim jeszcze uruchomi się tail. To idzie mniej więcej tak:

  1. Powłoka otwiera >plik wyjściowy do zapisu, obcinając go
  2. Powłoka ustawia się tak, aby mieć deskryptor pliku 1 (dla standardowego wyjścia) dla tego wyjścia
  3. Powłoka wykonuje się tail.
  4. tailuruchamia się, otwiera /home/pi/Documents/testi niczego tam nie znajduje

Istnieją różne rozwiązania, ale kluczem jest zrozumienie problemu, co właściwie idzie nie tak i dlaczego.

To da ci to, czego szukasz,

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

Objaśnienie:

  • $() nazywa się zastępowaniem poleceń, które wykonuje tail -n 50 /home/pi/Documents/test
  • cudzysłowy zachowują podział wiersza na wyjściu.
  • > /home/pi/Documents/testprzekierowuje wyjście echo "$(tail -n 50 /home/pi/Documents/test)"do tego samego pliku.
Rahul
źródło
Dziękuję, że działa dobrze! Mam jeszcze jedno pytanie. Czy możesz wyjaśnić, jak krok po kroku działa Twoja procedura?
dorinand
1
Ale dlaczego w twoim przypadku bash nie wykonuje się najpierw? Nie rozumiem, jak bash przetwarza polecenie. Czy ktoś może wyjaśnić?
dorinand
1
Symbol> znajduje się na poleceniu echo, więc jest wykonywany, gdy polecenie echo rozpoczyna wykonywanie. Nie można rozpocząć wykonywania przed napisaniem. Podstawienie zmiennej jest tym, co zapisuje polecenie. Uruchamia polecenie zagnieżdżone i tworzy polecenie echo przez podstawienie wartości.
jobermark
Gdy próbowałem użyć tego samego dla pliku dziennika 44 GB przy użyciu 5000 wierszy zamiast 50, pojawia się błądbash: xrealloc: cannot allocate 18446744071562067968 bytes
Carmageddon
8

Innym rozwiązaniem przekierowania pliku najpierw wyczyszczającego plik jest użycie spongez moreutilspakietu w następujący sposób:

tail -n 50 /home/pi/Documents/test | sponge /home/pi/Documents/test
iobender
źródło
6

Wynika to z tego, że bash przetwarza przekierowanie z >pierwszym, usuwając zawartość pliku. Następnie wykonuje polecenie. Gdybyś używał >>, ostatnie 50 wierszy zostanie dołączonych na końcu tego, co aktualnie znajduje się w pliku. W takim przypadku powtórzyłbyś dwa razy te same 50 wierszy.

Polecenie działa zgodnie z oczekiwaniami podczas przekierowywania do innego pliku. Oto jeden ze sposobów zapisania ostatnich 50 wierszy pliku do pliku o tej samej nazwie:

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

Najpierw zapisuje ostatnie 50 wierszy do pliku tymczasowego, który jest następnie przenoszony przy użyciu mvzastępowania oryginalnego pliku.

Jak zauważono w komentarzach, to nie zadziała, jeśli plik jest nadal otwarty. Przeniesienie pliku powoduje także utworzenie nowego i-węzła i może zmienić własność i uprawnienia. Lepszym sposobem na to przy użyciu pliku tymczasowego byłoby:

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

Plik tymczasowy można również usunąć, ale za każdym razem jego zawartość zostanie zastąpiona.

clk
źródło
Dziękuję Ci. Czy możesz mi wyjaśnić krok po kroku, co wykonać bash? Nie mogę sobie wyobrazić, jak to działa.
dorinand
tail -50 /home/pi/Documents/test >/tmp/foo && cat /tmp/foo >/home/pi/Documents/test
steve
1
zwróć uwagę, że to nie zadziała, jeśli plik dziennika jest nadal otwarty przez proces logowania (który będzie kontynuował logowanie do oryginalnego, usuniętego pliku). tempfile + move powoduje powstanie nowego i-węzła (zerwanie jakichkolwiek twardych dowiązań) i ewentualnie innego właściciela lub perms. tail ... > temp ; cat temp > orig ; rm -f tempPrace.
cas
4

Ponieważ widziałeś główny problem z przekierowaniem powłoki, oto alternatywny sposób przycinania pliku do jego ostatnich 50 wierszy:

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

Ciężką pracę wykonuje (GNU) sed z -ifunkcją „edycji na miejscu”, która działa pod przykryciem, tworząc wyjście w pliku tymczasowym. Reszta linii konfiguruje matematykę dla operacji sed, a mianowicie:

  1. policz linie w pliku ( wc), a następnie odejmij 50; przypisz to do n.
  2. jeśli n jest dodatnia, uruchom polecenie sed, aby usunąć linie od 1 do n.
Jeff Schaller
źródło
4
printf '%s\n' '1,$-50d'   w | ed -s /home/pi/Documents/tes

printfsłuży do wprowadzania poleceń (po jednym w wierszu) do ed. Te edpolecenia są:

  • 1,$-50d - usuń wszystkie oprócz ostatnich 50 wierszy
  • w - zapisz zmodyfikowany plik z powrotem na dysk

Nie ma żadnych przekierowań, więc powłoka nie może nadpisać pliku wyjściowego przed jego odczytaniem.

Ponadto, w przeciwieństwie do większości form edycji „na miejscu” (które zwykle symulują edycję „na miejscu”, tworząc plik tymczasowy, a następnie zmieniając jego nazwę na oryginał), edtak naprawdę edytuje plik oryginalny - więc zachowuje ten sam i-węzeł ( oraz właściciel, grupa i uprawnienia - plik tymczasowy + mv zawsze zmieni i-węzeł i może zmienić inne w zależności od okoliczności).

cas
źródło
4

Na nieco innej ścieżce możesz logrotate(8)regularnie tworzyć kopie zapasowe plików dziennika w plikach o nazwach przyrostowych, a następnie usuwać stare.

W ten sposób zarządza się głównymi plikami dziennika systemowego, aby nie rosły zbyt długo.

CSM
źródło