与Popen()子进程的读写

15

我正在尝试使用Python的subprocess.Popen()调用与子进程通信。在我的真实代码中,我正在实现一种IPC类型,因此我想写入一些数据,读取响应,再写入一些数据,再读取响应,以此类推。因此,我无法使用Popen.communicate(),因为它只适用于简单情况。

以下代码显示了我的问题。它甚至无法获得第一个响应,在第一个“Reading result”处挂起。为什么?如何使其按照我的期望工作?

import subprocess
p = subprocess.Popen(["sed", 's/a/x/g'],
                     stdout = subprocess.PIPE,
                     stdin = subprocess.PIPE)

p.stdin.write("abc\n")
print "Reading result:"
print p.stdout.readline()

p.stdin.write("cat\n")
print "Reading result:"
print p.stdout.readline()
2个回答

7

sed的输出是有缓冲区的,只有当累积了足够的数据或者输入流被耗尽并关闭后才会输出其数据。

试试这个:

import subprocess
p = subprocess.Popen(["sed", 's/a/x/g'],
                     stdout = subprocess.PIPE,
                     stdin = subprocess.PIPE)

p.stdin.write("abc\n")
p.stdin.write("cat\n")
p.stdin.close()

print "Reading result 1:"
print p.stdout.readline()

print "Reading result 2:"
print p.stdout.readline()

请注意,对于大数据而言,使用stdin写入时会在缓冲区满时阻塞,因此无法可靠地完成。最佳方法是使用communicate()


但是,当直接在shell中运行命令时,sed并不会以这种方式运行。如果您这样做,响应将在每行之后出现。即使我在写入后调用p.stdin.flush(),问题仍然存在。此外,在实际生活中,我也编写了被调用的程序,没有缓冲区,并且它的行为方式相同。我并不认为缓冲是这里的问题所在。 - Mats Ekberg
添加 bufsize=0 可以解决你的问题。subprocess.Popen(['sed', '-l', 's/a/x/g'], bufsize=0, stdin=PIPE, stdout=PIPE) - codeskyblue

5

如果可以,我建议尝试使用Popen().communicate(),因为它可以为您提供很多方便之处。但是,如果您需要按照您描述的确切方式使用Popen(),则需要使用-l选项将sed设置为在换行符后刷新其缓冲区:

p = subprocess.Popen(['sed', '-l', 's/a/x/g'],
                     stdout=subprocess.PIPE,
                     stdin=subprocess.PIPE)

你的代码应该可以正常工作


1
确实有效!我的sed由于某些原因使用了“-u”表示“无缓冲”,而不是“-l”,但它仍然起作用。这解决了我的示例代码,但不幸的是并没有解决我的真实代码问题,因为实际命令不是sed,而是另一个Python程序。不过你的回复很好,你指出了问题所在。 - Mats Ekberg
1
好的,问题解决了。问题出在结果的输出缓冲区。在我的子进程中执行一个简单的 stdout.flush() 就解决了这个问题。谢谢! - Mats Ekberg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接