Опорожнение файла без нарушения записи на него

У меня есть программа, выход которой я перенаправляю в файл журнала:

./my_app > log 

Я хотел бы время от времени очищать (т.е. пустым) журнал (по требованию) и пробовать различные вещи, например

 cat "" > log 

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

Есть ли способ сделать это?

Обновить

Обратите внимание, что я не могу изменить приложение, производящее выход. Он просто выплевывает его в stdout, и я хочу сохранить его в журнале, чтобы я мог его проверять, когда мне это нужно, и очистить его, когда захочу. Однако мне не нужно перезапускать приложение.

5 Solutions collect form web for “Опорожнение файла без нарушения записи на него”

Другая форма этой проблемы возникает при использовании давно работающих приложений, чьи журналы периодически вращаются. Даже если вы перемещаете исходный журнал (например, mv log.txt log.1 ) и немедленно заменяете его файлом с тем же именем, перед тем как произойдет фактическое ведение журнала, если процесс держит файл открытым, он либо закончит писать к log.1 (потому что это все еще может быть открытым inode) или ни к чему.

Обычный способ справиться с этим (сам системный регистратор работает таким образом) заключается в реализации обработчика сигнала в процессе, который закроет и снова откроет свои журналы. Затем, когда вы хотите переместить или очистить (удалив) журнал, отправьте этот сигнал сразу после этого процесса.

Вот простая демонстрация для bash – простите мои грубые навыки оболочки (но если вы собираетесь редактировать это для лучших практик и т. Д., Пожалуйста, убедитесь, что вы сначала поняли функциональность и протестировали свою ревизию перед редактированием):

 #!/bin/bash trap sighandler INT function sighandler () { touch log.txt exec &> log.txt } echo $BASHPID exec &> log.txt count=0; while [ $count -lt 60 ]; do echo "$BASHPID Count is now $count" sleep 2 ((count++)) done 

Начните это путем разметки в фоновом режиме:

 > ./test.sh & 12356 

Обратите внимание, что он сообщает свой PID терминалу, а затем начинает регистрацию в log.txt . У вас теперь есть 2 минуты, чтобы поиграть. Подождите несколько секунд и попробуйте:

 > mv log.txt log.1 && kill -s 2 12356 

Просто kill -2 12356 может работать и на вас здесь. Сигнал 2 – это SIGINT (это также то, что делает Ctrl-C, поэтому вы можете попробовать это на переднем плане и перенести или удалить файл журнала с другого терминала), который trap должна ловушка. Проверять;

 > cat log.1 12356 Count is now 0 12356 Count is now 1 12356 Count is now 2 12356 Count is now 3 12356 Count is now 4 12356 Count is now 5 12356 Count is now 6 12356 Count is now 7 12356 Count is now 8 12356 Count is now 9 12356 Count is now 10 12356 Count is now 11 12356 Count is now 12 12356 Count is now 13 12356 Count is now 14 

Теперь давайте посмотрим, все ли он записывается в log.txt хотя мы переместили его:

 > cat log.txt 12356 Count is now 15 12356 Count is now 16 12356 Count is now 17 12356 Count is now 18 12356 Count is now 19 12356 Count is now 20 12356 Count is now 21 

Заметьте, что он продолжал идти туда, где он остановился. Если вы не хотите, чтобы запись просто очищала журнал, удалив его

 > rm -f log.txt && kill -s 2 12356 

Проверьте:

 > cat log.txt 12356 Count is now 29 12356 Count is now 30 12356 Count is now 31 12356 Count is now 32 12356 Count is now 33 12356 Count is now 34 12356 Count is now 35 12356 Count is now 36 

Продолжается.

Вы не можете сделать это в сценарии оболочки для выполняемого подпроцесса, к сожалению, потому что, если он находится на переднем плане, собственные обработчики сигналов bash ( trap s) приостанавливаются, и если вы его разворачиваете в фоновом режиме, вы не можете переназначить его выход. То есть, это то, что вы должны реализовать в своем приложении.

Однако…

Если вы не можете изменить приложение (например, потому что вы его не пишите), у меня есть утилита CLI, которую вы можете использовать в качестве посредника. Вы также можете реализовать простую версию этого в скрипте, который служит в качестве канала для журнала:

 #!/bin/bash trap sighandler INT function sighandler () { touch log.txt exec 1> log.txt } echo "$0 $BASHPID" exec 1> log.txt count=0; while read; do echo $REPLY done 

Назовем это pipetrap.sh . Теперь нам нужна отдельная программа для тестирования, имитирующая приложение, которое вы хотите зарегистрировать:

 #!/bin/bash count=0 while [ $count -lt 60 ]; do echo "$BASHPID Count is now $count" sleep 2 ((count++)) done 

Это будет test.sh :

 > (./test.sh | ./pipetrap.sh) & ./pipetrap.sh 15859 

Это два отдельных процесса с отдельными PID. Чтобы очистить test.sh , который pipetrap.sh через pipetrap.sh :

 > rm -f log.txt && kill -s 2 15859 

Проверьте:

 >cat log.txt 15858 Count is now 6 15858 Count is now 7 15858 Count is now 8 

15858, test.sh , все еще запущен и его выход регистрируется. В этом случае никаких изменений в приложении не требуется.

TL; DR

Откройте файл журнала в режиме добавления :

 cmd >> log 

Затем вы можете безопасно обрезать его с помощью:

 : > log 

Детали

С Bourne-подобной оболочкой есть 3 основных способа, которыми файл может быть открыт для записи. Только в режиме записи ( > ), read + write ( <> ) или append (и только для записи, >> ).

В первых двух ядрах ядро ​​запоминает текущую позицию, которую вы (я имею в виду, открытое описание файла , разделяемое всеми файловыми дескрипторами, которые дублируются или унаследованы им путем форматирования от того, на котором вы открыли файл) находятся в файл.

Когда вы выполните:

 cmd > log 

log открыт в режиме только для записи оболочкой для stdout из cmd .

cmd (его начальный процесс, порожденный оболочкой и всеми возможными дочерними элементами) при записи на их stdout, напишите в текущей позиции курсора, хранящейся в открытом файле, которое они разделяют в этом файле.

Например, если cmd изначально записывает zzz , позиция будет в смещении байта 4 в файл, а в следующий раз, когда cmd или его дети будут записываться в файл, это то, где данные будут записаны независимо от того, вырос ли файл или уменьшился в интервале.

Если файл сжат, например, если он был усечен с помощью

 : > log 

и cmd пишет xx , те xx будут записаны со смещением 4 , а первые 3 символа будут заменены символами NUL.

 $ exec 3> log # open file on fd 3. $ printf zzz >&3 $ od -c log 0000000 zzz 0000003 $ printf aaaa >> log # other open file description -> different cursor $ od -c log 0000000 zzzaaaa 0000007 $ printf bb >&3 # still write at the original position $ od -c log 0000000 zzzbbaa 0000007 $ : > log $ wc log 0 0 0 log $ printf x >&3 $ od -c log 0000000 \0 \0 \0 \0 \0 x 0000006 

Это означает, что вы не можете обрезать файл, который был открыт в режиме только для записи (и это то же самое для read + write ), как если бы вы делали, процессы, в которых были открыты файловые дескрипторы, оставят символы NUL в начале файл (те, за исключением OS / X, обычно не занимают места на диске, хотя они становятся разреженными файлами).

Вместо этого (и вы заметите, что большинство приложений делают это при записи в файлы журналов), вы должны открыть файл в режиме добавления :

 cmd >> log 

или

 : > log && cmd >> log 

если вы хотите начать с пустого файла.

В режиме добавления все записи производятся в конце файла независимо от того, где была последняя запись:

 $ exec 4>> log $ printf aa >&4 $ printf x >> log $ printf bb >&4 $ od -c log 0000000 aaxbb 0000005 $ : > log $ printf cc >&4 $ od -c log 0000000 cc 0000002 

Это также более безопасно, как если бы два процесса открывали (таким образом) файл по ошибке (например, если вы запустили два экземпляра одного и того же демона), их вывод не будет перезаписывать друг друга.

В последних версиях Linux вы можете проверить текущую позицию и открыть ли дескриптор файла в режиме добавления , посмотрев /proc/<pid>/fdinfo/<fd> :

 $ cat /proc/self/fdinfo/4 pos: 2 flags: 0102001 

Или с:

 $ lsof +f G -p "$$" -ad 4 COMMAND PID USER FD TYPE FILE-FLAG DEVICE SIZE/OFF NODE NAME zsh 4870 root 4w REG 0x8401;0x0 252,18 2 59431479 /home/chazelas/log ~# lsof +fg -p "$$" -ad 4 COMMAND PID USER FD TYPE FILE-FLAG DEVICE SIZE/OFF NODE NAME zsh 4870 root 4w REG W,AP,LG 252,18 2 59431479 /home/chazelas/log 

Эти флаги соответствуют флагам O …_, переданным open системному вызову.

 $ gcc -E - <<< $'#include <fcntl.h>\nO_APPEND O_WRONLY' | tail -n1 02000 01 

( O_APPEND – 0x400 или октал 02000)

Итак, оболочка >> открывает файл с O_WRONLY|O_APPEND (и 0100000 здесь O_LARGEFILE, который не имеет отношения к этому вопросу), а > только O_WRONLY<> только O_RDWR ).

Если вы выполните:

 sudo lsof -nP +fg | grep ,AP 

для поиска файлов, открытых с помощью O_APPEND , вы найдете большинство файлов журналов, которые в настоящее время открыты для записи в вашей системе.

Если я правильно понимаю, tee кажется разумным подходом:

 $ ./myapp-that-echoes-the-date-every-second | tee log > /dev/null & [1] 20519 $ head log Thu Apr 3 11:29:34 EDT 2014 Thu Apr 3 11:29:35 EDT 2014 Thu Apr 3 11:29:36 EDT 2014 $ > log $ head log Thu Apr 3 11:29:40 EDT 2014 Thu Apr 3 11:29:41 EDT 2014 Thu Apr 3 11:29:42 EDT 2014 

В качестве быстрого решения вы можете использовать журнал с вращением (например, ежедневное вращение):

 date=`date +%Y%m%d` LOGFILE=/home/log$date.log 

и перенаправлять на него ./my_app >> log$date.log

Это проблема, которая давно решена с помощью syslog (во всех ее вариантах), но есть два инструмента, которые могли бы решить вашу конкретную проблему с минимальными усилиями.

Первым, более портативным, но менее универсальным решением является logger (обязательный для любого инструментария администраторов). Это простая утилита, которая копирует стандартный ввод в syslog. (передавая доллар и делая поворот файла проблемой logrotate и syslog)

Второе, более элегантное, но менее портативное решение – это syslog-ng, который помимо приема сообщений журнала из стандартных сокетов syslog может выполнять программы, выходные данные которых фильтруются через регистратор. (Я еще не использовал эту функцию, но она отлично подходит для того, что вы хотите сделать.)

  • Почему трубопроводы и перенаправление работают с документами?
  • Перенаправление сообщений об ошибках syslogd в журнал ошибок или в нулевой файл?
  • Написать в файл без перенаправления?
  • Перенаправить ввод из файла, но показывать также в stdout?
  • Файловые дескрипторы и сценарии оболочки
  • Каково состояние стандартного потока ввода-вывода при выдаче команды?
  • Замена места для команды paste
  • Подстановка команд в качестве цели для перенаправления ввода-вывода
  • вывод в файл, затем использовать файл для ввода
  • Перенаправить stdout через ssh
  • whatis command Ошибка перенаправления выходных данных
  • Interesting Posts

    Помогла ли моя команда $ mv '* .mobi' соединить мои файлы?

    Bind Ctrl + Down в Joe на консоли Linux

    где поставить двоичные файлы, чтобы они всегда находились на пути и их можно было легко найти

    «Не удалось разрешить хост» при установке / обновлении пакетов yum

    Как переназначить сочетание клавиш для контекстного меню в Xubuntu

    Как использовать команду find для поиска нескольких расширений

    Запустить команду на вход и выход ssh при завершении команды

    Ошибка Debconf в системе с systemd

    Есть ли программное обеспечение для программирования игровых мышек Logitech для любого Linux Distro?

    Плохая таблица маршрутизации?

    Мне нужно извлечь IP-адрес и путь к файлу из отчета Nessus, используя текстовый обработчик

    Как я могу идти вперед в файле? Что такое oposite ctrl + O

    Запустите команду CDO "remapnn"

    Как удалить конкретный перевод xterm X11 по умолчанию?

    Как прочитать таблицу разделов in-memory (kernel) / dev / sda?

    Linux и Unix - лучшая ОС в мире.