Вопрос или проблема
У меня есть процесс, который выводит строку для каждого обновления прогресса (побочное замечание: он очищает/заменяет строку, не добавляя чистый перенос строки).
Я хочу сохранить последнюю строку этого процесса в выходной файл или обрезать выходной файл, чтобы контролировать его размер.
На данный момент у меня:
genrtr > genrtr.log
и с помощью cron я пытался использовать > genrtr.log, но это не работает. Также rm genrtr.log не помогает, потому что тогда процесс перестает обновлять файл.
Я понимаю, почему это не работает, но хочу понять, как это изменить, чтобы соответствовать моим нуждам.
Попробовал genrtr | sed -ne ‘$w genrtr.log’, но в этом случае запись в файл происходит только после завершения процесса.
Уточнения:
Процесс выдает данные каждую секунду, и если сервер не падает, процесс будет работать вечно.
Я интересовался, с какой частотой происходит запуск?
Попробуйте использовать tee для перенаправления вывода в лог-файл, это утилита, часто используемая для создания копии потока и перенаправления этой копии в выходной файл.
Используйте:
command | tee command_result.log
Вы можете создать функцию мониторинга в обёрточном скрипте, который вызывает эту программу и удаляет верхние 10 строк через некоторое время… таким образом, ваш лог-файл не будет занимать более нескольких килобайт.
Также, если между строками есть лишние пробелы, вы можете использовать утилиту перевода "tr" для сжатия…
пример:
Nitin@Kaizen ~
$ df -h | head -1
Filesystem Size Used Avail Use% Mounted on
Nitin@Kaizen ~
$ df -h | head -1 | tr -s ‘ ‘
Filesystem Size Used Avail Use% Mounted on *** обратите внимание на сжатие пробелов
Надеюсь, это поможет.
Есть несколько вещей, которые вы могли бы попробовать. Самый простой способ – просто напечатать последние строки файла:
tail genrtr.log
Затем, после завершения процесса удалите лог-файл. Другой вариант – периодически перезаписывать файл.
Запуск процесса в фоновом режиме:
genrtr > genrtr.log &
Перезапись содержимого лог-файла:
echo > genrtr.log
Файл теперь обрезан, но будет продолжать обновляться процессом genrtr, так что следующее обновление будет записано в него. Вы можете автоматизировать это, например, обрезая файл, если он становится больше 1 МБ:
while true;
do if [ $(stat -c%s genrtr.log) -gt 1000000 ]; then
tail genrt.log > /tmp/foo && cat /tmp/foo > genrt.log;
fi;
done
Этот маленький скрипт будет работать до тех пор, пока вы его не остановите (while true;), и каждый раз, когда genrtr.log становится больше одного мегабайта, он будет сохранять последние несколько строк и удалять остальные.
ОБНОВЛЕНИЕ:
Как очень правильно указал Скотт ниже, если ваш вывод содержит \r для очистки строки, tail не будет работать так, как ожидается. Это, однако, должно сработать:
while true;
do if [ $(stat -c%s genrtr.log) -gt 1000000 ]; then
tail genrt.log | perl -pe ‘s/.+\r(.+)/$1\n/’ > /tmp/foo && cat /tmp/foo > genrt.log;
fi;
done
Команда perl удаляет все перед последним \r и печатает последнюю "строку" (данные после \r и новую строку). Результат должен быть таким, что последняя строка сохраняется, остальная часть файла очищается, и файл продолжает наполняться.
Я считаю, что это будет довольно сложно сделать без изменения genrtr или написания новой программы. Если вам комфортнее сделать последнее, я предлагаю следующий план:
int c;
FILE *fp;
fp = fopen(log_file, "w");
if (fp == NULL) (обработка ошибок)
while ((c = getc()) != EOF)
{
putchar(c);
if (c == escape sequence, что заканчивает строку)
{
fflush(fp); //Вы, вероятно, должны проверить здесь ошибки.
rewind(fp);
}
else
putc(c, fp);
}
Это действует как комбинация tee и tail – читает стандартный ввод и записывает его в стандартный вывод и файл – с разницей в том, что он оставляет только последнюю строку в файле.
Затем вы бы запускали
genrtr | вышеупомянутая_программа
Решение
В данный момент у меня есть genrtr > genrtr.log, и с помощью cron я пытался использовать > genrtr.log, но это не работает.
Этот подход можно легко исправить, используя genrtr >> … вместо genrtr > … (при этом в cron используется > для обрезки файла).
Разница объясняется в другом моем ответе, который можно резюмировать следующей фразой:
>> — это по сути "всегда искать конец файла", тогда как > поддерживает указатель на последнее записанное местоположение.
Прочтите связанный ответ, он полностью соответствует вашей ситуации.
Побочное замечание
Также rm genrtr.log не помогает, потому что тогда процесс перестает обновлять файл.
Строго говоря, он не перестает обновлять файл. Файл отвязывается от каталога, но он все еще открыт, все еще записывается в него, он продолжает занимать все больше и больше места в файловой системе.
Любой новый файл – это другой файл, даже если он занимает тот же путь. Процесс не обновляет новый файл, потому что никогда не открывает его, он даже не замечает его. Файловый дескриптор, используемый процессом, все еще ведет к старому (удаленному) файлу.
Ответ или решение
Чтобы решить задачу управления выходным потоком процесса в файл, сохраняя последнюю строку и поддерживая размер файла в пределах разумного, вам потребуется разработать подход, который будет учитывать особенности вашей программы и систему вывода.
Проблема:
Ваш процесс каждые секунду выводит строку с обновлёнными данными. Вместо создания новой строки, вывод замещает предыдущую, используя символы возврата каретки (\r
). Вы хотите сохранить именно последнюю версию этой строки в лог-файле, поддерживая его размер в разумных пределах.
Решение:
-
Использование команды
tee
:
tee
позволяет выводить данные как на экран, так и в файл одновременно. Однако,tee
сам по себе не решает проблему очистки файла.genrtr | tee >(awk '{ print $0 > "genrtr.log" }')
Этот способ сохраняет каждый новый вывод в
genrtr.log
. -
Создание скрипта-монитора для управления размером файла:
Чтобы автоматически поддерживать размер файла в разумных пределах и гарантировать сохранение только последней актуальной строки, воспользуйтесь циклом, который будет периодически проверять и сокраштать файл.
#!/bin/bash # Функция для извлечения последней актуальной строки function extract_latest_line { perl -p -e 's/.*\r(.+)/$1\n/' genrtr.log > /tmp/genrtr.log.tmp mv /tmp/genrtr.log.tmp genrtr.log } # Запуск процесса и использование функции для обновления лог-файла genrtr | (while true; do # Проверить размер файла и обновить его, если необходимо if [ $(stat -c%s "genrtr.log") -gt 1000000 ]; then extract_latest_line fi sleep 10 done) &
- *`perl -p -e ‘s/.\r(.+)/$1\n/’`**: Этот скрипт извлекает последнюю строку после символа возврата каретки и сохраняет её.
stat -c%s "genrtr.log"
: Определяет размер файла.sleep 10
: Число 10 указывает интервал проверки файла в секундах — вы можете настроить это значение под свои нужды.
-
Проблема с
rm genrtr.log
:
Удаление файла приводит к тому, что процесс продолжает запись в уже открытый и ныне удалённый файл. Чтобы это избежать, вы можете использовать подходecho "" > genrtr.log
, что фактически очистит файл, сохранив его в файловой системе.
Объяснение:
- >> и >: Разница между
>>
и>
заключается в том, что>>
всегда добавляет данные в конец файла, сохраняя прошлый вывод, в то время как>
перезаписывает файл, начиная с начала. - Обновление файла: вам не следует удалять или перезапускать процесс, чтобы избежать потери данных. Вместо этого безопасно управляйте файлом.
Резюме:
Эти методы позволяют вам поддерживать контроль над логированием длительного процесса, сохраняя недавние данные актуальными и устраняя ненужные старые записи. Это создаёт устойчивый, регулярный подход к управлению лог-файлами.