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

我有一个程序,需要被称为与Python的子进程。该程序已用java编写。是的,我知道...

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

不幸的是,当我使用通信[0]调用subprocess.popen2或subprocess.Popen时,当我使用子进程时,输出数据的大约10%会丢失。分配给stdout的AND会在我使用文件描述符(从开放返回)分配给标准输出。

子进程中的文档非常明确,使用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
...
2
额外 编辑
意见: 2
我们可以有一些Python代码吗?
额外 作者 Bittrance,
嘿,只是想捕捉stdout(而不是stderr)。输出是一个数字和行尾字符 - 它期望所有ascii输出
额外 作者 ct_,
@ jadkik94和保罗,所以我很欣赏你的时间,但你并没有真正的帮助。我已经说过PIPE在调用子进程时有问题(我已经多次阅读文档),所以你如何正确地做到这一点?
额外 作者 ct_,
@保罗。它没有读取所有的输出。我在等待1300行以上的新行字符 - 取决于输入。是的等待并不真正“等待”我的python脚本继续执行过去的地方,我分出了子进程。至于准确性,我正在解释我遇到的问题。
额外 作者 ct_,
@保罗不是我正在寻找的答案。如果它不稳定,我可以在哪里阅读有关如何正确执行此操作的信息。当我使用FD我仍然没有得到所有的数据 - 我怀疑分配一个FD到标准输出具有相同的问题,分配subprocess.PIPE标准输出。
额外 作者 ct_,
最后10%的产出。张贴了一个更新的问题来澄清。谢谢!
额外 作者 ct_,
哪个“10%”你错过了?它是在开始,结束?你期望什么产出?
额外 作者 Joel Cornett,
“但是等待并不等待java程序完成运行 - 它不会阻止”< - 也完全不准确。你确定你的子过程按照你期望的方式工作吗?
额外 作者 the paul,
你得到什么错误?
额外 作者 the paul,
对,subprocess.PIPE应该与 communicate()一起使用,否则要谨慎使用,以防止输入和输出fds互相阻塞。这部分是我“正确使用”的意思。
额外 作者 the paul,
“子流程中的文档非常明确,如果您试图捕获子进程的所有输出,则使用subprocess.PIPE是不稳定的。” < - 如果文件说明了这一点,那完全是错误的。 PIPE是非常安全的,如果使用得当,将会在连接的fd上获得所有输出。
额外 作者 the paul,
你确定你的java子进程本身不是分叉的吗?这也许可以解释为什么你的 wait()调用看起来没有被阻塞。
额外 作者 the paul,
我向你保证,如果你使用 communications 并且你的数据适合内存(如果没有,你会看到更明显的失败),PIPE没有“问题”。文档中的注释是为了防止人们试图以不恰当的方式使用它。我很想帮忙,但似乎你真的想责怪系统的错误部分。
额外 作者 the paul,
更具体地说,使用 subprocess.PIPE 或将fd分配给子进程的输出基本上与shell执行输出重定向至文件时所执行的操作完全相同(操作系统的 dup2( )系统调用)。您可以放心地假定该部件正在工作。你可以尝试在你的命令结尾添加“`| tee outputcopy ”;那么你可以检查 outputcopy`是否有你期望的所有行。如果没有,也许你的Java程序工作不正确。
额外 作者 the paul,
@ jadkik94在这里不太可能成为问题; “几千字”的每行几个字符可以很容易地放入内存中,在任何可以运行Python或Java的可以想象的机器上。
额外 作者 the paul,
是否有可能将某些输出写入stderr?
额外 作者 Jeremiah,
“注意不要在此函数中使用stdout = PIPE或stderr = PIPE,因为在当前进程中没有读取管道,如果子进程产生足够的输出到管道来填充OS管道缓冲区,则可能会阻塞子进程。来自子流程文档
额外 作者 jadkik94,
有关大数据的沟通中存在警告,但它仍然存在对于另一种选择是非常不清楚的...
额外 作者 jadkik94,
看看是否可以提供帮助:另一个SO问题
额外 作者 jadkik94,

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正在做它的工作。

2
额外
我会稍微旋转一下,并发布结果,谢谢!
额外 作者 ct_,

我在 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%可能在你的缓冲区没有正确的刷新。
2
额外
会马上回到你身边 - 一点点工作jdi的笔记。谢谢!
额外 作者 ct_,