我有一个长期运行的 python 脚本,它会定期将数据输出到标准输出,我使用如下方式调用它:
python script.py > output.txt
这个脚本已经运行了一段时间,我想用Ctrl+停止它C,但不丢失任何输出。不幸的是,当我实现脚本时,我忘记在每行输出后刷新缓冲区,例如sys.stdout.flush()
(先前建议我需要一个强制输出刷新的解决方案),所以现在调用Ctrl+C将导致我丢失所有输出。
如果想知道是否有任何方法可以与正在运行的 Python 脚本(或更一般地说,正在运行的进程)交互以强制其刷新其输出缓冲区。我不是问如何编辑并重新运行脚本以使其正确刷新——这个问题具体是关于与正在运行的进程交互(并且,在我的情况下,不会丢失当前代码执行的输出)。
答案1
如果如果你真的想要这些数据,我建议附上数据库调试器到 Python 解释器,暂时停止任务,调用fsync(1)
(标准输出),脱离该进程(恢复该进程)并仔细检查输出文件。
查看/proc/$(pidof python)/fd
有效的文件描述符。$(pidof x)
返回名为“ ”的进程的 PID x
。
# your python script is running merrily over there.... with some PID you've determined.
#
# load gdb
gdb
#
# attach to python interpreter (use the number returned by $(pidof python))
attach 1234
#
# force a sync within the program's world (1 = stdout, which is redirected in your example)
call fsync(1)
#
# the call SHOULD have returned 0x0, sync successful. If you get 0xffffffff (-1), perhaps that wasn't stdout. 0=stdin, 1=stdout, 2=stderr
#
# remove our claws from poor python
detach
#
# we're done!
quit
我曾用这种方法更改工作目录、即时调整设置……很多事情。唉,你只能调用在运行程序中定义的函数,fsync
不过效果很好。
(gdb 命令“ info functions
”将列出所有可用的函数。不过要小心。你正在操作居住在一个流程上。
还有一个命令peekfd
(可在psmisc
Debian Jessie 和其他软件包中找到),它可让您查看进程缓冲区中隐藏的内容。同样,/proc/$(pidof python)/fd
它将向您显示可作为 peekfd 参数的有效文件描述符。
如果你不记得-u
Python 了,你可以在命令前加上stdbuf
(在 中coreutils
,已经安装)根据需要将 stdin/stdout/stderr 设置为无缓冲、行缓冲或块缓冲:
stdbuf -i 0 -o 0 -e 0 python myscript.py > unbuffered.output
当然,man pages
是你的朋友,嘿!也许别名在这里也很有用。
alias python='python -u'
现在你的 python 总是可以-u
用于你所有的命令行工作!
答案2
首先确保你有 Python 的调试符号(或者至少是 glibc)。Fedora1您可以使用以下方法安装它们:
dnf debuginfo-install python
然后附加数据库到运行脚本并运行以下命令:
[user@host ~]$ pidof python2
9219
[user@host ~]$ gdb python2 9219
GNU gdb (GDB) Fedora 7.7.1-13.fc20
...
0x00007fa934278780 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:81
81 T_PSEUDO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS)
(gdb) call fflush(stdout)
$1 = 0
(gdb) call setvbuf(stdout, 0, 2, 0)
$2 = 0
(gdb) quit
A debugging session is active.
Inferior 1 [process 9219] will be detached.
Quit anyway? (y or n) y
Detaching from program: /usr/bin/python2, process 9219
这将冲洗标准输出并禁用缓冲。2
从setvbuf
在我的系统上,call 是 的值_IONBF
。您需要找出您的系统上是什么( agrep _IONBF /usr/include/stdio.h
应该可以解决问题)。
根据我在实施过程中看到的情况PyFile_SetBufSize
和PyFile_WriteString
在 CPython 2.7 中,它应该可以运行得很好,但我不能做出任何保证。
1 Fedora 包含一种特殊类型的 RPM,称为调试信息 rpms。这些自动创建的 RPM 包含来自程序文件的调试信息,但移至外部文件中。
答案3
您的当前问题没有解决方案。如果您的脚本已经启动,则事后您无法更改缓冲模式。这些都是内存缓冲区,所有这些都是在脚本启动、文件句柄打开、管道创建等时设置的。
作为一种长远考虑,当且仅当所讨论的部分或全部缓冲在输出的 IO 级别完成时,您才可以执行命令sync
;但在这种情况下通常不太可能发生这种情况。
将来,您可以使用 Python 的-u
选项*来运行脚本。通常,许多命令都有特定于命令的选项来禁用 stdin/stdout 缓冲,并且您还可以使用包unbuffer
中的命令获得一些通用成功expect
。
当程序中断时,A Ctrl+将导致系统级缓冲区被刷新C除非缓冲是由 Python 本身完成的,它尚未实现使用Ctrl+刷新其自身缓冲区的逻辑C。暂停、崩溃或终止不会如此友善。
*强制 stdin、stdout 和 stderr 完全不缓冲。
答案4
看来我太过谨慎,担心在运行 Ctrl-C 后会因缓冲输出而丢失数据;根据这个帖子如果我的程序正常退出,那么缓冲区应该会被刷新,如果我按 Ctrl-C 就会出现这种情况。另一方面,如果我使用 SIGKILL 或类似命令终止脚本,我将丢失缓冲的输出。