subprocess stdin PIPE在程序终止之前不会返回

时间:2014-09-22 00:45:37

标签: python windows python-3.x subprocess

我一直在尝试使用子进程对subprocess.PIPE进行故障排除而没有运气。

我正在尝试将命令传递给始终在运行的进程,并且无需每次都关闭/打开进程即可接收结果。

以下是主要的启动代码:

launcher.py:

import subprocess
import time

command = ['python', 'listener.py']
process = subprocess.Popen(
    command, bufsize=0,
    stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
)

# simulates sending a new command every 10 seconds
for x in range(1,10):
    process.stdin.write(b'print\r\n')
    process.stdin.flush()
    time.sleep(10)

listener.py:

import sys

file = open('log.txt', 'w+')
while True:
   file.write(sys.stdin.read(1))
file.close()

这是简化以显示相关的部分。最后我会有线程监听stdout和stderr但是现在我正在尝试解决基础问题。

我期望发生的事情:对于launcher.py中的每个循环,listener.py中的file.write()都会写入。 相反的是:当循环关闭并且程序终止时,所有内容都会写入,或者我的SIGTERM / CTRL-C是脚本。

我在Windows 8 Python 3.4中运行它。

这几乎就像stdin缓冲,直到进程关闭然后它通过。我有buffsize = 0设置,我正在冲洗,所以这对我没有意义。我认为其中任何一个都足够了。

子进程在不同的进程中运行,因此启动器中的sleep应该对子进程没有影响。

有没有人有任何想法阻止它的原因?

更新1: 从控制台(python.exe stdinreader.py)

运行以下代码也会出现相同的行为

也就是说,当您在程序运行时键入控制台时,不会向该文件写入任何内容。

stdinreader.py:

import sys
import os

file = open('log.txt', 'w+b')
while True:
    file.write(sys.stdin.read(1))
file.close()

在file.write()之前添加一个file.flush()解决了这个问题,但这对我的子进程没有帮助,因为我无法控制子进程如何刷新(这将是我的返回子进程.PIPE )。也许如果我用open('wb')重新初始化PIPE,它就不会缓冲。我会试试。

更新2: 我似乎已经将这个问题隔离到被调用的子进程,它在写入stdout后没有刷新。

在没有修改子进程的情况下,我可以做些什么来强制父和子之间的stdout PIPE刷新?子进程是运行args ['-script的magick.exe(imagemagick 7) ,' - ']。从子进程的角度来看,它有一个stdout对象< _io.TextIOWrapper name =''mode ='w'coding ='cp1252'>。我想子进程只会在初始化时打开默认的stdout对象,我们无法真正控制它是否缓冲。

奇怪的是,传递子进程正常的sys.stdout对象而不是subprocess.PIPE not 在写入后需要将子进程设置为.flush()。

3 个答案:

答案 0 :(得分:3)

程序的运行方式不同,具体取决于它们是从控制台运行还是通过管道运行。如果控制台(python进程可以使用os.stdin.isatty()检查),stdout数据是行缓冲的,你会立即看到数据。如果一个管道,stdout数据是块缓冲的,你只看到数据堆积很多或程序刷新管道。

当您想要获取程序输出时,您必须使用管道并且程序以缓冲模式运行。在linux上,你可以通过创建一个假的控制台(伪tty,pty,...)来欺骗程序。 pty模块,pexpect和其他人这样做。

在Windows上,我不知道有什么方法可以让它工作。如果您控制正在运行的程序,请经常刷新它。否则,在Windows徽标上徒劳地眩光。如果您希望提前结束,您甚至可以在下次相亲时提及问题。但我想不出更多。

(如果有人知道修复,我想听听它。我看到一些试图打开Windows控制台和屏幕的代码,但这些解决方案不断丢失数据。如果有某处有一个环回字符设备。)

答案 1 :(得分:2)

问题是在写入stdout后被调用的子进程没有刷新。感谢J.F.和Tdelaney指出我正确的方向。我在这里向开发人员提出了这个问题:http://www.imagemagick.org/discourse-server/viewtopic.php?f=2&t=26276&p=115545#p115545

除了更改子进程源之外,在Windows中似乎没有解决此问题的方法。也许如果你将子进程的输出重定向到可能有效的NamedTemporaryFile,但我还没有对它进行测试,我认为它将被锁定在Windows中,因此父进程和子进程中只有一个可以立即打开它。不是不可克服但令人讨厌。可能还有一种方法可以通过stdbuf的unixutils端口执行应用程序,或类似J.F.建议的那样:Python C program subprocess hangs at "for line in iter"

如果您有权访问您正在调用的子进程的源代码,则可以始终在禁用缓冲的情况下重新编译它。在C:

中禁用stdout上的缓冲非常简单
setbuf(stdout, NULL)

或设置每行缓冲而不是块缓冲:

setvbuf(stdout, (char *) NULL, _IOLBF, 0);

另请参阅:Python C program subprocess hangs at "for line in iter"

希望这可以帮助其他人。

答案 2 :(得分:-1)

你可以尝试在listener.py结束时关闭管道吗?我认为这是问题