Як зберегти останні 50 рядків у лог-файлі


22

Я намагаюся зберігати останні 50 рядків у своєму файлі, де я щохвилини зберігаю температуру. Я використав цю команду:

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

Але результат - порожній тестовий файл. Я думав, він перелічить останні 50 рядків тестового файлу і вставить його в тестовий файл. Коли я використовую цю команду:

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

це добре працює. У файлі test2 є 50 рядків.

Хтось може мені пояснити, де проблема?


2
Щось на зразок rrdtool може бути більш підходящим для зберігання N записів (серед інших статистичних даних) протягом часу.
трилик


2
питання класичного укорочення
haylem

Якщо ви використовуєте python для генерування ваших журналів, слід заглянути в loggingмодуль
Wayne Werner

Відповіді:


30

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

  1. Оболонка відкриває >вихідний файл для запису, обрізаючи його
  2. Оболонка встановлюється для того, щоб файл-дескриптор 1 (для stdout) використовувався для цього виводу
  3. Оболонка виконується tail.
  4. tailбіжить, відкривається /home/pi/Documents/testі там нічого не знаходить

Є різні рішення, але ключовим є розуміння проблеми, що насправді відбувається не так і чому.

Це призведе до того, що ви шукаєте,

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

Пояснення:

  • $() називається підстановка команд, яка виконується tail -n 50 /home/pi/Documents/test
  • лапки зберігають розриви рядків у висновку.
  • > /home/pi/Documents/testперенаправляє вихід echo "$(tail -n 50 /home/pi/Documents/test)"до того ж файлу.

Дякую, це добре працює! У мене є ще одне питання. Чи можете ви пояснити, як ваша процедура працює поетапно?
dorinand

1
Але чому у вашому випадку bash не виконується> спочатку? Я не розумію, як баш обробляє команду. Хтось може пояснити?
дорінанд

1
Значення> знаходиться в команді echo, тому воно виконується, коли команда echo починає виконання. Він не може розпочати виконання до того, як буде записано. Підстановка змінної - це те, що пише команда. Він запускає вкладену команду і створює команду echo, замінюючи значення.
jobermark

Коли я намагався використати те саме для файлу журналу 44 Гб, використовуючи 5000 рядків замість 50, я отримую помилкуbash: xrealloc: cannot allocate 18446744071562067968 bytes
Carmageddon

8

Іншим рішенням для попереднього очищення файлу для перенаправлення файлів є використання spongeтакого moreutilsпакета:

tail -n 50 /home/pi/Documents/test | sponge /home/pi/Documents/test

6

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

Команда працює, як очікувалося, при переадресації до іншого файлу. Ось один із способів записати останні 50 рядків у файл з однойменним файлом:

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

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

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

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

Тимчасовий файл також можна видалити, хоча кожного разу його вміст буде перезаписуватися.


Дякую тобі. Не могли б ви пояснити мені крок за кроком, що виконують баш? Я не уявляю, як це працює.
Дорінанд

tail -50 /home/pi/Documents/test >/tmp/foo && cat /tmp/foo >/home/pi/Documents/test
steve

1
зауважте, що це не буде працювати, якщо файл журналу все ще відкритий в процесі реєстрації (який продовжить вести журнал до вихідного, видаленого файлу). tempfile + переміщення призводить до нового inode (порушення будь-яких жорстких посилань) та, можливо, іншого власника або пермі. tail ... > temp ; cat temp > orig ; rm -f tempпрацює.
cas

4

Оскільки ви бачили головну проблему з перенаправленням оболонки, ось альтернативний спосіб обрізати файл до останніх 50 рядків:

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

Важка робота виконується (GNU) sed з -iфункцією "на місці редагування", яка працює під обкладинками, створюючи вихід у тимчасовому файлі. Решта рядків задають математику для роботи sed, а саме:

  1. порахуйте рядки у файлі ( wc), потім відніміть 50; призначити це n.
  2. якщо n є позитивним, запустіть команду sed для видалення рядків 1 по n.

4
printf '%s\n' '1,$-50d'   w | ed -s /home/pi/Documents/tes

printfвикористовується для введення команд (по одному на рядок) в ed. Ці edкоманди є:

  • 1,$-50d - видалити всі, крім останніх 50 рядків
  • w - записати модифікований файл назад на диск

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

Крім того, на відміну від більшості форм "на місці" редагування (які, як правило, лише імітують "на місці" редагування, створюючи тимчасовий файл, а потім перейменуючи його на оригінал), edнасправді редагує оригінальний файл - таким чином він зберігає той самий inode ( і власник, група та дозволи - tempfile + mv завжди змінить inode, а може змінити інших залежно від обставин).


4

На дещо іншій доріжці ви можете logrotate(8)регулярно створювати резервні копії файлів журналів на поступово названі файли, а потім видаляти старі.

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

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