Jak zachować tylko ostatnie n wierszy pliku dziennika?


18

Skrypt, który napisałem, coś robi, a na końcu dołącza kilka wierszy do własnego pliku dziennika. Chciałbym zachować tylko ostatnie n linii (powiedzmy 1000 linii) pliku dziennika. Można to zrobić na końcu skryptu w następujący sposób:

tail -n 1000 myscript.log > myscript.log.tmp
mv -f myscript.log.tmp myscript.log

ale czy istnieje bardziej czyste i eleganckie rozwiązanie? Być może dokonano tego za pomocą jednego polecenia?


logrotateto eleganckie rozwiązanie
Ipor Sircer 19.09.16

1
Myślałem o tym, ale konfiguracja Logrotate byłaby dłuższa niż sam skrypt ...
dr01

Jeśli logrotate to przesada, Twoje rozwiązanie jest tak eleganckie, jak tylko się da. Z sed / awk możesz to zrobić w jednym wierszu, ale nie bez pliku tymczasowego, więc prawdopodobnie nie jest on bardziej wydajny i prawdopodobnie mniej czytelny.
kba stoi z Monicą

Odpowiedzi:


28

Jest to możliwe, ale jak powiedzieli inni, najbezpieczniejszą opcją jest wygenerowanie nowego pliku, a następnie przeniesienie tego pliku w celu zastąpienia oryginału.

Poniższa metoda ładuje wiersze do BASH, więc w zależności od liczby wierszy tail, wpłynie to na użycie pamięci przez lokalną powłokę do przechowywania zawartości wierszy dziennika.

Poniżej usuwa również puste wiersze, jeśli istnieją na końcu pliku dziennika (z powodu zachowania oceny BASH "$(tail -1000 test.log)"), więc nie zapewnia naprawdę 100% dokładnego obcięcia we wszystkich scenariuszach, ale w zależności od sytuacji może być wystarczające.

$ wc -l myscript.log
475494 myscript.log

$ echo "$(tail -1000 myscript.log)" > myscript.log

$ wc -l myscript.log
1000 myscript.log

Mądry. Oznacziłem to jako zaakceptowaną odpowiedź, ponieważ nie wymaga instalacji dodatkowych narzędzi. Chciałbym zaakceptować odpowiedź zarówno twoją, jak i @ John1024.
dr01

Twoja decyzja. Poprosiłem o rozwiązanie gąbki, ponieważ nie wiedziałem o tym i gwarantuje się, że nie zadziera z pustymi liniami dziennika. To rozwiązanie może to zrobić, w zależności od zawartości pliku dziennika.
parkamark

To rozwiązanie ma warunek wyścigu. Jeśli masz pecha, przekierowanie do pliku następuje przed odczytem z pliku i kończy się to pustym plikiem.
Coroos

21

Narzędzie spongejest przeznaczone tylko do tego przypadku. Jeśli masz go zainstalowanego, możesz napisać dwa wiersze:

tail -n 1000 myscript.log | sponge myscript.log

Zwykle czytanie z pliku w tym samym czasie, w którym piszesz do niego, jest zawodne. spongerozwiązuje to, nie pisząc do, myscript.logdopóki tailnie skończy czytać i zakończy potoku.

zainstalować

Aby zainstalować spongew systemie podobnym do Debiana:

apt-get install moreutils

Aby zainstalować spongew systemie RHEL / CentOS, dodaj repozytorium EPEL, a następnie:

yum install moreutils

Dokumentacja

Od man sponge:

spongeodczytuje standardowe wejście i zapisuje je w określonym pliku. W przeciwieństwie do przekierowania powłoki, spongepochłania wszystkie dane wejściowe przed zapisaniem pliku wyjściowego. Umożliwia to tworzenie potoków, które odczytują i zapisują do tego samego pliku.


2
+1 Dzięki, nie wiedziałem sponge. Bardzo przydatny dla wszystkich, którzy nauczyli się trudnej drogi, której nie da się zrobić sort importantfile.txt > importantfile.txt:)
dr01

4

zdecydowanie „tail + mv” jest znacznie lepszy! Ale dla gnu sed możemy spróbować

sed -i -e :a -e '$q;N;101,$D;ba' log

3

Dla przypomnienia, edmożesz zrobić coś takiego

ed -s infile <<\IN
0r !tail -n 1000 infile
+1,$d
,p
q
IN

Otworzy się infilei rzablokuje na wyjściu tail -n 1000 infile(tj. Wstawi to wyjście przed 1. linią), a następnie usunie z tego, co początkowo było 1. linią do końca pliku. Wymienić ,pz wedytować plik w miejscu.
Pamiętaj jednak, że edrozwiązania nie są odpowiednie dla dużych plików.


0

W skrypcie możesz zastosować logikę rotacji logów. Wykonaj wszystkie logowanie za pomocą funkcji:

log()
{
   ...
}

Ta funkcja po pierwsze działa w następujący sposób:

printf "%s\n" "$*" >> logfile

następnie sprawdza rozmiar pliku lub w jakiś sposób decyduje, że plik wymaga rotacji. W tym momencie plik logfile.1, jeśli istnieje, jest usuwany, plik logfile.0, jeśli istnieje, zostaje przemianowany na logfile.1i logfilezostaje przemianowany na logfile.0.

Decyzja, czy obrócić, może być oparta na liczniku utrzymywanym w samym skrypcie. Gdy osiągnie 1000, jest resetowane do zera.

Jeśli wymagane jest zawsze ścisłe przycięcie do 1000 wierszy, skrypt może zliczyć liczbę wierszy w pliku dziennika podczas uruchamiania i odpowiednio zainicjować licznik (lub jeśli liczba już osiąga lub przekracza 1000, wykonaj obrót natychmiast).

Lub możesz uzyskać rozmiar, na przykład za pomocą wc -c logfilei wykonaj obrót w oparciu o przekroczenie określonego rozmiaru. W ten sposób plik nigdy nie musi być skanowany w celu ustalenia warunku.


0

Zrobiłem użycie zamiast mvThe cppolecenie, aby osiągnąć to, że jesteś w stanie mieć jakieś logi prawo w miejscu, w którym program jest uruchomiony. Może w innym katalogu użytkownika domowego lub w katalogu aplikacji i mieć wszystkie dzienniki w jednym miejscu jako linki dowiązane. Jeśli użyjesz mvpolecenia, utracisz twardy link. Jeśli cpzamiast tego użyjesz polecenia, zachowasz ten twardy link.

mój kod jest jak:

TMP_FILE="$(mktemp "${TMPFILENAME}.XXX")"

for FILE in "${LOGFILE_DIR}"/* ; do
    tail -n $MAXLINES "${FILE}" > "${TMP_FILE}"
    if [ $(ls -g "${TMP_FILE}" | awk '{print $4}') -lt $(ls -g "${FILE}" | awk '{print $4}') ] ; then
        cp "${TMP_FILE}" "${FILE}"
    fi
done   

Więc jeśli pliki znajdują się w tym samym systemie plików, możesz również dać użytkownikom różne prawa i ${LOGFILE_DIR}modyfikować długość tak jak ja.

Jeśli jest to mvpolecenie, tracisz dowiązanie twarde między plikami, więc twój drugi plik nie jest bardziej połączony z pierwszym - być może umieścił gdzieś indziej.

Jeśli w innym miejscu nie pozwalasz komuś na usunięcie pliku, twoje dzienniki pozostają razem i są dobrze kontrolowane za pomocą własnego skryptu.

logrotatemoże milszy. Ale cieszę się z tego rozwiązania.

Nie przejmuj się „”, ale w moim przypadku są jakieś pliki ze spacjami i innymi specjalnymi literami, a jeśli nie wykonam „” lub {}, cała partia nie będzie ładna.

Na przykład istnieje katalog, w którym starsze pliki są automatycznie spakowane do pliku, OLDFILE.zipa wszystko, co jest spakowane, jest również wymienione w pliku, .zip_logwięc .zip_logjest również w tym katalogu, ale w LOGFILE_DIRMam:

ln .zip_log "${LOGFILE_DIR}/USER_ZIP_log"

równy plik, ponieważ jest to twardy link.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.