Як припинити команду Linux tee, не вбиваючи програму, яку вона отримує


19

У мене є скрипт bash, який працює до тих пір, поки працює машина Linux. Я запускаю його, як показано нижче:

( /mnt/apps/start.sh 2>&1 | tee /tmp/nginx/debug_log.log ) &

Після запуску я можу побачити команду tee в моєму PS- висновку, як показано нижче:

$ ps | grep tee
  418 root       0:02 tee /tmp/nginx/debug_log.log
3557 root       0:00 grep tee

У мене є функція, яка контролює розмір журналу, який виробляє tee, і вбиває команду tee, коли журнал досягає певного розміру:

monitor_debug_log_size() {
                ## Monitor the file size of the debug log to make sure it does not get too big
                while true; do
                                cecho r "CHECKING DEBUG LOG SIZE... "
                                debugLogSizeBytes=$(stat -c%s "/tmp/nginx/debug_log.log")
                                cecho r "DEBUG LOG SIZE: $debugLogSizeBytes"
                                if [ $((debugLogSizeBytes)) -gt 100000 ]; then
                                                cecho r "DEBUG LOG HAS GROWN TO LARGE... "
                                                sleep 3
                                                #rm -rf /tmp/nginx/debug_log.log 1>/dev/null 2>/dev/null
                                                kill -9 `pgrep -f tee`
                                fi
                                sleep 30
                done
}

На мій подив, вбивство команди tee також вбиває екземпляр start.sh. Чому це? Як я можу закінчити команду tee, але продовжувати запускати мій start.sh? Спасибі.

Відповіді:


34

Після teeзакінчення команда, що подає, буде продовжувати виконуватись, поки не спробує записати більше результатів. Тоді він отримає SIGPIPE (13 для більшості систем) для спроби запису в трубку без читачів.

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


А ще краще, а не вбивати tee взагалі, використовувати logrotateз copytruncateопцією для простоти.

Цитувати logrotate(8):

copytruncate

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


9
Ви також хочете використовувати tee -aдля teeвідкриття файлу в режимі додавання, інакше трійник буде продовжувати записувати у файл з тим же зрушенням після того, як ви врізаєте його (і в системах, які не підтримують розріджені файли, як на macOS, які будуть перерозподіліть розділ файлу, що веде до цього положення, зайнявши вдвічі більше місця на диску).
Стефан Шазелас

4
Іншим варіантом було б передати logger -sна syslog, щоб подбати про ведення журналу ( -sтакож друкувати на stderr).
Стефан Шазелас

1
+1 для logrotate. Чудова програма
Дмитро Кудрявцев

2
Або в системі, що використовує systemd і journald, systemd-cat замість реєстратора. Тоді ви отримуєте безліч фільтрів і обертів на виході безкоштовно.
Зан Лінкс

3

Пояснення "Чому"

Якщо коротко: Якщо помилка запису не призвела до виходу програми (за замовчуванням), у нас буде безлад. Подумайте find . | head -n 10- ви не хочете findпродовжувати працювати, скануючи решту вашого жорсткого диска, після того, як headвже взяли 10 необхідних рядків і продовжили роботу.

Робити це краще: поверніть всередину свого реєстратора

Розглянемо наступне, яке взагалі не використовується tee, як показовий приклад:

#!/usr/bin/env bash

file=${1:-debug.log}                     # filename as 1st argument
max_size=${2:-100000}                    # max size as 2nd argument
size=$(stat --format=%s -- "$file") || exit  # Use GNU stat to retrieve size
exec >>"$file"                           # Open file for append

while IFS= read -r line; do              # read a line from stdin
  size=$(( size + ${#line} + 1 ))        # add line's length + 1 to our counter
  if (( size > max_size )); then         # and if it exceeds our maximum...
    mv -- "$file" "$file.old"            # ...rename the file away...
    exec >"$file"                        # ...and reopen a new file as stdout
    size=0                               # ...resetting our size counter
  fi
  printf '%s\n' "$line"                  # regardless, append to our current stdout
done

Якщо запустити як:

/mnt/apps/start.sh 2>&1 | above-script /tmp/nginx/debug_log

... це почнеться шляхом додавання до /tmp/nginx/debug_logперейменування файлу, /tmp/nginx/debug_log.oldколи вміст вмісту перевищує 100 КБ. Оскільки сам реєстратор здійснює обертання, під час обертання немає зламаної труби, помилок та вікна втрати даних - кожен рядок буде записаний у той чи інший файл.

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

  • svlogd, службовий реєстратор із набору Runit.
  • s6-log, активно підтримувана альтернатива від набору сканетів.
  • multilog від DJB Daemontools, онука цього сімейства інструментів нагляду та моніторингу.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.