首页 文章

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

提问于
浏览
2

我一直试图用subprocesses解决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应该对子进程没有影响 .

有没有人有任何想法,为什么这是阻止?

Update 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()解决了这个问题,但这对我的子进程没有帮助,因为我无法控制子进程如何刷新(这将是我的返回subprocess.PIPE) . 也许如果我用open('wb')重新初始化那个PIPE,它就不会缓冲 . 我会尝试 .

Update 2: 我似乎已将此问题隔离到被调用的子进程,该进程在写入stdout后没有刷新 .

Is there anything I can do to force a flush on the stdout PIPE between parent and child without modifying the subprocess? 子进程是magick.exe(imagemagick 7)运行args ['-script, ' - ']. From the point of view of the subprocess it has a stdout object of <_io.TextIOWrapper name=' ' mode=' w ' encoding=' cp1252 '>. I guess the subprocess will just open the default stdout objects on initialization and we can' t真正控制它是否缓冲 .

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

3 回答

  • 2

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

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

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

    (如果有人知道修复,我想听听 . 我已经看到一些试图打开Windows控制台和屏幕的代码,但这些解决方案不断丢失数据 . 如果有环回字符,它应该可以工作那里的设备) .

  • -1

    问题是被写入的子进程在写入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

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

相关问题