Python 子进程丢失了程序标准输出的 10%

Posted

技术标签:

【中文标题】Python 子进程丢失了程序标准输出的 10%【英文标题】:Python subprocess losing 10% of a program's stdout 【发布时间】:2012-05-28 05:36:38 【问题描述】:

我有一个程序需要作为 python 的子进程调用。该程序是用java编写的。是的,我知道...

无论如何,我需要捕获该程序的所有输出。

不幸的是,当我使用communicate[0] 调用subprocess.popen2 或subprocess.Popen 时,当我使用分配给stdout 的subprocess.PIPE 和使用时,我丢失了大约10% 的输出数据分配给标准输出的文件描述符(从打开返回)。

subprocess 中的文档非常明确,如果您尝试捕获子进程的所有输出,则使用 subprocess.PIPE 是不稳定的。

我目前正在使用 pexpect 将输出转储到 tmp 文件中,但由于显而易见的原因,这需要永远。

我想将所有数据保存在内存中以避免磁盘写入。

欢迎任何建议!谢谢!

import subprocess

cmd = 'java -Xmx2048m -cp "/home/usr/javalibs/class:/home/usr/javalibs/libs/dependency.jar" --data data --input input" 

# doesn't get all the data
#
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
output = p.communicate()[0]

OR
# doesn't get all the data
#
fd = open("outputfile",'w')
p = subprocess.Popen(cmd, stdout=fd, shell=True)
p.communicate()
fd.close() # tried to use fd.flush() too.

# also tried
# p.wait() instead of p.communicate(), but wait doesn't really wait for the java program to finish running - it doesn't block

OR
# also fails to get all the data
#
import popen2
(rstdout, rstdin) = popen2.popen2(cmd)

预期输出是一系列 ascii 行(几千行)。这些行包含一个数字和一个行尾字符

0\n
1\n
4\n
0\n
...

【问题讨论】:

是否有可能将某些输出写入标准错误? 我们可以提供一些 Python 代码吗? 你错过了哪“10%”?是开始还是结束?你期待什么输出? 你确定你的 java 子进程本身没有分叉吗?这或许可以解释为什么您的 wait() 呼叫似乎没有被阻止。 更具体地说,使用subprocess.PIPE 或将 fd 分配给子进程的输出与您的 shell 在执行输出重定向到文件时所做的完全相同(操作系统的 dup2() 系统称呼)。您可以放心地假设该部分正在工作。您可以尝试添加 "` | tee outputcopy" at the end of your command there; then you could check that outputcopy` 具有您期望的所有行。如果没有,可能您的 java 程序运行不正确。 【参考方案1】:

我在stdout 上使用过具有更大输出的subprocess,但还没有看到这样的问题。很难从您所展示的内容中得出根本原因是什么。我会检查以下内容:

因为p.wait() 不适合你。可能的情况是,当您阅读 PIPE 时,您的 java 程序仍在忙于打印最后 10%。先直接获取p.wait()

在阅读 PIPE 之前插入足够长的等待时间(比如 30 秒),你的 10% 会出现吗? 值得怀疑的是p.wait() 不会阻塞你的java 程序。您的 java 程序是否进一步子处理其他程序? 检查p.wait()的返回值。您的 java 程序是否正常终止?

如果问题不在于您的并发模型,请检查您是否在 java 程序中正确打印:

你在你的java程序中使用了什么函数来打印到stdout?它是否倾向于或忽略IOException? 您是否正确刷新了流?当您的 java 程序终止时,最后 10% 可能在您的缓冲区中,而没有适当的刷新。

【讨论】:

会尽快与您联系 - 稍后会处理 jdi 的笔记。谢谢!【参考方案2】:

它必须与您实际调用的进程相关。您可以通过使用另一个回显行的 python 脚本进行简单测试来验证这一点:

out.py

import sys

for i in xrange(5000):
    print "%d\n" % i

sys.exit(0)

test.py

import subprocess

cmd = "python out.py"
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
output = p.communicate()[0]

print output

因此,您可以验证问题不是数据的大小,而是与您正在调用的进程的通信。

您还应该确认您正在运行的 python 版本,因为我已经阅读了有关 Popen 内部缓冲区的过去问题(但使用您建议的单独文件句柄通常为我修复)。

如果子进程调用无限期挂起,这将是一个缓冲区问题。但如果这个过程正在完成,只是缺少线条,那么 Popen 就在做它的工作。

【讨论】:

以上是关于Python 子进程丢失了程序标准输出的 10%的主要内容,如果未能解决你的问题,请参考以下文章

Python、子进程、管道和选择

Jupyter notebook 中 Python 子进程的实时标准输出输出

如何读取子进程标准输出的第一个字节,然后在 Python 中丢弃其余字节?

如何读取子进程标准输出的第一个字节,然后在 Python 中丢弃其余字节?

Python:如何写入子进程的标准输入并实时读取其输出

node.js python子进程不会实时打印