意外删除了正在运行的进程的日志文件python something.py 2>&1 | tee .log
。脚本正在 zsh 上的 tmux 窗格中运行。进程仍在运行但未记录日志。输出本身溢出了 tmux-scrollback-buffer。我能否以某种方式(管理员/sudo 权限)重新启动日志记录进程而无需重新启动进程?
通常我的尝试不会出现问题,代码与安全或任何类型的生产无关,只是复杂的数学计算。因此,这种尝试一直都足够了。
就我目前的情况而言,如果我可以无需重新启动该过程即可再次开始记录,那就太好了。
答案1
只要进程tee
持有打开的文件描述符,该文件就会继续存在,并且所有内容仍会记录在那里。您可以通过 /proc 复制来恢复其当前内容:
查找“tee”进程的 PID。
使用
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 技巧来恢复旧日志文件(通过另一个 shell,而不是在 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
函数定义文件,这使得 open() 调用的行为类似于>>
shell 运算符。)对于诸如“tee”之类的简单情况,open() 极不可能为您提供除原始 #3 之外的任何其他文件描述符,因为这是最低的可用 FD。但在某些情况下,对于更复杂的程序(如果存在编号间隙),可能需要调用
dup2($2, 3)
并close($2)
手动将新打开的文件移动到所需的 FD。旧文件现在将完全消失(因为它已被删除和由于最后一个文件句柄已关闭,所以“tee”会在没有注意到任何内容的情况下写入新文件。
注意:它不会打开新文件,而是可能可以使用linkat()
它来在不中断任何操作的情况下恢复原始日志文件,但我还没有测试过。(编辑:不幸的是,根据 linkat() 文档,这对于已完全解除链接的文件不起作用。)