Случайно удалил файл журнала запущенного процесса `python something.py 2>&1 | tee .log`. Есть ли способ сохранить вывод на панели tmux?

Случайно удалил файл журнала запущенного процесса `python something.py 2>&1 | tee .log`. Есть ли способ сохранить вывод на панели tmux?

Случайно удален файл журнала запущенного процесса python something.py 2>&1 | tee .log. Скрипт запущен в панели tmux на zsh. Процесс все еще запущен, но не ведет журнал. Сам вывод переполняет буфер tmux-scrollback-buffer. Могу ли я как-то (права администратора/sudo) снова запустить процесс журналирования, не перезапуская процесс?

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

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

решение1

Файл продолжает существовать, пока teeпроцесс удерживает открытый файловый дескриптор, и все по-прежнему регистрируется там. Вы можете восстановить его текущее содержимое, скопировав его через /proc:

  1. Найдите PID процесса «tee».

  2. Используйте lsfd -p <PID>или lsof -p <PID>, ls -l /proc/<PID>/fdчтобы найти номер дескриптора файла, соответствующий открытому файлу. (Он даже будет отмечен как «(удалено)» рядом с именем файла.)

    В простых программах, таких как «tee», первым открытым файлом почти всегда будет FD #3, поэтому все примеры в этом посте будут использовать 3его.

  3. Скопируйте содержимое файла в новый файл с помощью /proc:

    cp /proc/<TEE_PID>/fd/3 old.log
    

    (Символические ссылки в /proc/PID/fd являются особенными — их открытие все равно приводит к правильному файлу, даже если символическая ссылка выглядит поврежденной или указывает на что-то, что даже не является настоящим файлом.)

Также можно заставить «tee» начать запись в новый файл:

  1. Подключите gdbотладчик к процессу:

    $ sudo gdb -p <TEE_PID>
    

    Это приостановит 'tee'. Программа Python также может быть приостановлена, если она выведет достаточно лог-вывода для заполнения буфера конвейера (иначе она этого не заметит).

  2. Если вы этого еще не сделали, воспользуйтесь трюком /proc, чтобы восстановить старый файл журнала (через другую оболочку, а не из gdb):

    $ cp /proc/<TEE_PID>/fd/3 old.log
    

    Делая этопослеgdb подключен (т.е. пока 'tee' приостановлен), вы можете избежать потери сообщений в промежутке между 'cp' и open().

  3. Теперь используйте 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.

  4. Старый файл теперь полностью исчезнет (так как он был удаленипоследний дескриптор файла был закрыт), но «tee» будет записывать данные в новый файл, ничего не замечая.

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

Связанный контент