
Случайно удален файл журнала запущенного процесса python something.py 2>&1 | tee .log
. Скрипт запущен в панели tmux на zsh. Процесс все еще запущен, но не ведет журнал. Сам вывод переполняет буфер tmux-scrollback-buffer. Могу ли я как-то (права администратора/sudo) снова запустить процесс журналирования, не перезапуская процесс?
Обычно моя попытка проходит без проблем, и код не имеет отношения к безопасности или какому-либо производству, а просто сложные математические вычисления. Поэтому эта попытка всегда была достаточной.
В моем текущем случае было бы здорово, если бы я мог снова начать ведение журнала, не перезапуская процесс.
решение1
Файл продолжает существовать, пока tee
процесс удерживает открытый файловый дескриптор, и все по-прежнему регистрируется там. Вы можете восстановить его текущее содержимое, скопировав его через /proc:
Найдите PID процесса «tee».
Используйте
lsfd -p <PID>
илиlsof -p <PID>
,ls -l /proc/<PID>/fd
чтобы найти номер дескриптора файла, соответствующий открытому файлу. (Он даже будет отмечен как «(удалено)» рядом с именем файла.)В простых программах, таких как «tee», первым открытым файлом почти всегда будет FD #3, поэтому все примеры в этом посте будут использовать
3
его.Скопируйте содержимое файла в новый файл с помощью
/proc
:cp /proc/<TEE_PID>/fd/3 old.log
(Символические ссылки в /proc/PID/fd являются особенными — их открытие все равно приводит к правильному файлу, даже если символическая ссылка выглядит поврежденной или указывает на что-то, что даже не является настоящим файлом.)
Также можно заставить «tee» начать запись в новый файл:
Подключите
gdb
отладчик к процессу:$ sudo gdb -p <TEE_PID>
Это приостановит 'tee'. Программа Python также может быть приостановлена, если она выведет достаточно лог-вывода для заполнения буфера конвейера (иначе она этого не заметит).
Если вы этого еще не сделали, воспользуйтесь трюком /proc, чтобы восстановить старый файл журнала (через другую оболочку, а не из gdb):
$ cp /proc/<TEE_PID>/fd/3 old.log
Делая этопослеgdb подключен (т.е. пока 'tee' приостановлен), вы можете избежать потери сообщений в промежутке между 'cp' и open().
Теперь используйте gdb, чтобы закрыть «tee» и снова открыть файл:
(gdb) p (int) close(3) $1 = 0 (gdb) p (int) open("new.log", 01|0100|02000, 0666) $2 = 3 (gdb) q Detach? y
(Значения
01|0100|02000
равныO_WRONLY|O_CREAT|O_APPEND
отfcntl.h, что заставляет вызов open() вести себя как>>
оператор оболочки.)Для простых случаев, таких как 'tee', крайне маловероятно, что open() даст вам какой-либо другой файловый дескриптор, кроме исходного #3, поскольку это наименьший свободный FD. Но в некоторых ситуациях с более сложными программами (если есть пробел в нумерации) может потребоваться вызвать
dup2($2, 3)
иclose($2)
вручную переместить вновь открытый файл в нужный FD.Старый файл теперь полностью исчезнет (так как он был удаленипоследний дескриптор файла был закрыт), но «tee» будет записывать данные в новый файл, ничего не замечая.
Примечание: вместо открытия нового файла, онможетможно использовать linkat()
для создания исходного файла журнала, не прерывая ничего, но я пока не проверял это. (Изменение: к сожалению, согласно документации linkat(), это не работает конкретно для файлов, которые стали полностью несвязанными.)