Як зберігати лише останні n рядків файлу журналу?


18

Сценарій, який я написав, щось робить і, врешті-решт, додає рядки до власного журналу. Я хотів би зберегти лише останні п рядків (скажімо, 1000 рядків) журналу. Це можна зробити в кінці сценарію таким чином:

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

але чи є більш чисте і елегантне рішення? Можливо, виконується за допомогою однієї команди?


logrotateце елегантне рішення
Ipor Sircer

1
Я думав про це, але конфігурація logrotate була б довшою, ніж сам сценарій ...
dr01

Якщо логротат є надмірним, ваше рішення настільки ж елегантне, як виходить. З sed / awk ви можете зробити це в одному рядку, але не без тимчасового файлу, тому він, мабуть, не є більш ефективним і, мабуть, менш читабельним.
kba стоїть з Монікою

Відповіді:


28

Це можливо так, але, як вже говорили інші, найбезпечнішим варіантом є створення нового файлу, а потім переміщення цього файлу для перезапис оригіналу.

Наведений нижче метод завантажує рядки в BASH, тому залежно від кількості рядків tail, це вплине на використання пам'яті локальної оболонки для зберігання вмісту ліній журналу.

Нижче також видаляються порожні рядки, якщо вони існують в кінці файлу журналу (через поведінку оцінювання BASH "$(tail -1000 test.log)"), тому не дає справді 100% точного усічення у всіх сценаріях, але, в залежності від вашої ситуації, може бути достатньо.

$ wc -l myscript.log
475494 myscript.log

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

$ wc -l myscript.log
1000 myscript.log

Розумний. Я позначив це як прийняту відповідь, оскільки не потребує встановлення додаткових інструментів. Я б хотів, щоб я міг прийняти і вашу відповідь, і відповідь @ John1024.
dr01

Твій дзвінок. Я підтримав рішення губки, оскільки не знав про це, і гарантовано не возитися з порожніми лініями журналу. Це рішення може зробити це залежно від вмісту файлу журналу.
parkamark

Це рішення має перегони. Якщо вам не пощастило, перенаправлення -into- файл відбувається перед читанням -від- файлу, і ви закінчитесь із порожнім файлом.
Coroos

21

Утиліта spongeпризначена саме для цього випадку. Якщо він встановлений, то можна записати два рядки:

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

Зазвичай читати з файлу одночасно, коли ви до нього пишете, ненадійно. spongeвирішує це, не записуючи до myscript.logтих пір, поки tailне закінчить його читання та припинив трубку.

Встановити

Щоб встановити spongeсистему, схожу на Debian:

apt-get install moreutils

Щоб встановити spongeв системі RHEL / CentOS, додайте EPEL-репо і виконайте наступні дії:

yum install moreutils

Документація

Від man sponge:

spongeзчитує стандартний вхід і записує його у вказаний файл. На відміну від перенаправлення оболонки, spongeзамочує весь її вхід до запису вихідного файлу. Це дозволяє побудувати трубопроводи, які читаються з та записуються в один і той же файл.


2
+1 Спасибі, я не знав sponge . Дуже корисно для всіх, хто навчився важкого шляху, якого не в змозі зробити sort importantfile.txt > importantfile.txt:)
dr01

4

напевно "хвіст + mv" набагато краще! Але для gnu sed ми можемо спробувати

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

3

Для запису, edви могли б зробити щось на кшталт

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

Це відкривається infileі rвиводиться у вихідний файл tail -n 1000 infile(тобто він вставляє цей вихід перед 1-м рядком), а потім видаляє з того, що спочатку був першим рядком до кінця файлу. Замініть ,pна, wщоб змінити файл на місці.
Майте на увазі, що edрішення не підходять для великих файлів.


0

Що ви можете зробити у своєму сценарії - це реалізувати логіку обертання журналу. Зробіть весь журнал за допомогою функції:

log()
{
   ...
}

Ця функція, по-перше, виконує щось на кшталт:

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

потім він перевіряє розмір файлу або якимось чином вирішує, що файл вимагає обертання. У цей момент файл logfile.1, якщо він існує, видаляється, файл logfile.0, якщо він існує, перейменовується вlogfile.1 і logfileперейменовується в logfile.0.

Вирішення питання про те, чи потрібно обертати, може базуватися на лічильнику, який підтримується у самому сценарії. Коли вона досягає 1000, вона скидається до нуля.

Якщо завжди строга обрізка до 1000 рядків є вимогою, сценарій може підрахувати кількість рядків у файлі журналу, коли він починається, і відповідно ініціалізувати лічильник (або якщо кількість вже відповідає або перевищує 1000, зробіть обертання негайно).

Або ви могли отримати розмір, наприклад, з wc -c logfileротацією та робити обертання на основі перевищення певного розміру. Таким чином файл ніколи не повинен скануватися, щоб визначити стан.


0

Я замість цього mvвикористовував cpкоманду, щоб досягти того, щоб ви могли мати деякі логіни прямо там, де працює програмне забезпечення. Можливо, в іншому домашньому режимі dir або в dir додатку, і вони мають усі журнали в одному місці як жорсткі посилання. Якщо ви використовуєте mvкоманду, ви втрачаєте жорстке посилання. Якщо ви cpзамість цього використовуєте команду, ви збережете це жорстке посилання.

мій код приблизно такий:

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   

Отже, якщо файли перебувають у тій самій файловій системі, ви можете також надати деякі різні права користувачам та в ${LOGFILE_DIR} ви змінюєте довжину, як і я.

Якщо це mvкоманда, ви втрачаєте жорстке посилання між файлами, і тому ваш другий файл більше не підключений до першого - можливо, розміщується десь ще.

Якщо з іншого місця ви не дозволяєте комусь стерти файл, ваші журнали залишаються разом і підкоряються їм за допомогою власного сценарію.

logrotateможливо, приємніше. Але я задоволений цим рішенням.

Не турбуйте "", але в моєму випадку є деякі файли з пробілами та іншими спеціальними літерами, і якщо я не виконую "" навколо або {}, весь лот не працює добре.

Наприклад, є Dir, де старіші файли автоматизовано встромляються у файл, OLDFILE.zipі все, що отримується на блискавці, так само добре вказано у файлі, .zip_logтак що .zip_logє і в цій Dir, але в програмі " LOGFILE_DIRУ мене":

ln .zip_log "${LOGFILE_DIR}/USER_ZIP_log"

рівний файл, оскільки це жорстке посилання.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.