python - 将子进程 stdout 写入文件会截断为 4096 字节
问题描述
我正在尝试将标准输出写入文件以获取在子进程中运行的内容。我的问题是我的输出总是被截断为 4096 字节(一页大小),我认为这是由于 x86 系统上的 PIPE 大小造成的?
我像这样设置我的子流程
with open("newfile.txt", "w") as f:
proc = subprocess.Popen([find_executable(bin_name), *extra_args],
stdout=f, stderr=subprocess.DEVNULL)
让它在做其他事情的同时运行,然后
def shutdown(process):
if process.poll() is None:
try:
process.terminate()
process.wait(timeout.PROCESS_QUIT)
except subprocess.TimeoutExpired:
print(f"Process {process.args} not terminating after {timeout.PROCESS_QUIT} sec.",
file=sys.stderr)
process.kill()
newfile.txt 中的输出总是打印最多 4096 个字节然后截断(通常是中线)
我试过定期添加 context.file_to_close.flush() 但这似乎并没有任何影响。如何将整个标准输出打印到文件中?
strace 给了我 7000 多行的输出,我不太确定如何阅读它,但这似乎是与 newfile.txt 相关的所有内容?
openat(AT_FDCWD, "newfile.txt", O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0666) = 6
fstat(6, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
ioctl(6, TCGETS, 0x7ffc05c3fac0) = -1 ENOTTY (Inappropriate ioctl for device)
lseek(6, 0, SEEK_CUR) = 0
ioctl(6, TCGETS, 0x7ffc05c3f9c0) = -1 ENOTTY (Inappropriate ioctl for device)
lseek(6, 0, SEEK_CUR) = 0
stat("/basepath/project/build-behave/the_app.app", {st_mode=S_IFREG|0755, st_size=5355744, ...}) = 0
openat(AT_FDCWD, "/dev/null", O_RDWR|O_CLOEXEC) = 7
pipe2([8, 9], O_CLOEXEC) = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fb12fc44a10) = 1349
close(9) = 0
close(7) = 0
read(8, "", 50000) = 0
close(8) = 0
close(6) = 0
(根据 mata 和 Charles Duffy 的建议编辑代码)
解决方案
这里没有管道;Popen
只有通过它才能创建它们subprocess.PIPE
。
大多数编程语言都有一个标准库来缓冲输出到文件,以减少系统调用的数量(这是昂贵的)。4096 字节是缓冲区的常见默认大小。
如果进程正常退出,缓冲区中剩余的任何数据都会刷新到文件中。
您正在调用terminate
该过程。在 Windows 上,这会调用TerminateProcess
,它会立即终止进程而不会发出警告。在类 Unix 上,它发送SIGTERM
,可以通过刷新缓冲区等来处理,但仅仅因为它可以被处理并不意味着它会被处理。
您需要解决阻止进程退出的问题,然后无条件地等待它自行退出,或者向它发出信号以它可以理解的方式刷新其缓冲区。你做这些事情的方式取决于你正在运行的进程。
我试过添加 context.file_to_close.flush()
假设context.file_to_close
与 相同f
,这不会做任何事情,因为它刷新 Python 进程中的缓冲区,而不是子进程中的缓冲区。
(顺便说一句,您以后可能不需要保存f
和关闭它。您可以在Popen
返回后立即关闭它,因为此时子进程有自己的文件句柄。)
来自评论:
该过程是否向 stderr 写入了很多内容,您是否正在阅读它?如果您在进程运行时没有使用它,您的问题可能是进程在写入该管道时阻塞。如果您不阅读它,请将其发送到 subprocess.DEVNULL。
如果您通过了,这确实可以防止进程退出stderr=subprocess.PIPE
(就像您在问题的第一个版本中所做的那样)。
但是将其替换为stderr=subprocess.DEVNULL
意味着您不会看到子进程正在打印的任何错误消息。可能会有一条消息解释为什么它没有退出。
最好stderr
完全省略该参数,以便子进程继承您的stderr
,至少用于测试。
推荐阅读
- java - 是否可以从 Spring 应用程序上下文中获取 basePackages 值?
- scala - 如何根据条件对列表的元素进行分组?
- ruby-on-rails - ActionController::UrlGenerationError: 没有路由匹配 {:action=>"show", :controller=>"demographic_detail", :id=>nil}
- javascript - 在mongodb中将字符串转换为日期?
- sapui5 - 如何在调试器中查看 view.xml 文件?
- java - Json数组不在jsp中打印值
- c++ - 用信号和槽连接多个 UI 对象
- python-3.x - 带有 Python 3.7、pandas 和 scikit-learn (Windows 10) 的 Conda 环境 - 依赖项错误
- ios - Objective c中的自动滚动收集视图
- python - Python 日志记录调试级别不会消失