【问题标题】:About piping stdio and subprocess.Popen关于管道 stdio 和 subprocess.Popen
【发布时间】:2013-04-11 10:46:50
【问题描述】:

我有一个 Python 程序,它通过subprocess.Popen 打开另一个 Python 程序。第一个应该将一些文本输出到控制台(仅供参考),并将一些文本写入它产生的第二个程序。然后,它应该等待第二个程序响应(来自它的read()),并打印该响应。

第二个应该听第一个的输入(通过raw_input()),然后print 文本到第一个。

为了了解到底发生了什么,我在第二次延迟了 5 秒,结果让我有点吃惊。

代码如下:

import subprocess

print "1st starting."

app = subprocess.Popen("name", shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE) #<--- B

print "Writing something to app's STDIN..."
app.stdin.write(some_text)

print "Reading something from my STDIN..." #<--- A
result = app.stdout.read()

print "Result:"
print result

第二个:

import time

print "app invoked."

print "Waiting for text from STDIN..."
text = raw_input()

#process(text)
time.sleep(5)

print "magic"

当我运行这段代码时,它在 A 点暂停,因为那是最后一个控制台输出。

5 秒后,"Result:\n" 行将被输出,第二个程序具有 printed 的所有内容都会显示在控制台中。

为什么第一个程序在读取第二个程序的标准输出时会暂停?在读取其输出之前是否必须等待其子进程终止?如何更改它以便我可以在程序之间传递消息?

我正在运行 Debian Linux 7.0。

【问题讨论】:

    标签: python linux shell stdout stdin


    【解决方案1】:

    答案不在于任何与 subprocess 模块相关的魔法,而在于 Python 对象上 read() 方法的典型行为。

    如果你运行这个:

    import subprocess
    p = subprocess.Popen(['ls'], stdout=subprocess.PIPE)
    help(p.stdout.read)
    

    你会看到这个:

    读取(...)

    read([size]) -> read at most size bytes, returned as a string.
    
    If the size argument is negative or omitted, read until EOF is reached.
    Notice that when in non-blocking mode, less data than what was requested
    may be returned, even if no size parameter was given.
    

    (完)

    同样的事情适用于所有类似文件的对象。这很简单:不带参数调用read() 会消耗缓冲区,直到遇到错误(通常是EOF)。

    EOF 直到任何一个都不会发送:

    • 子进程调用 sys.stdout.close(),或者
    • 子进程退出,Python 运行时和/或操作系统内核清理其文件描述符

    注意os.read 有不同的行为 - 更像是 C 中典型的缓冲 I/O。内置的 Python 帮助功能是无用的,但如果你在任何 UNIXy 系统上,你应该能够运行 @987654329 @; Python 的行为或多或少与现有的行为相匹配。

    一句警告

    上面的程序很好,但是这样的模式有时会导致死锁。 subprocess 模块的文档在记录 Popen.wait() 的地方警告了这一点:

    警告

    这将在使用 stdout=PIPE 和/或 stderr=PIPE 时出现死锁,并且子进程会向管道生成足够的输出,从而阻塞等待 OS 管道缓冲区接受更多数据。使用communicate() 来避免这种情况。

    如果您在与子进程进行双向通信时不小心,可能会遇到类似的情况,具体取决于子进程正在做什么。

    编辑

    顺便说一句,this page 涵盖了带有 EOF 的管道的行为:

    如果所有引用管道写入端的文件描述符都已 关闭,然后尝试从管道读取(2)将看到文件结尾 (read(2) 将返回 0)。

    编辑 2

    正如 Lennart 上面提到的,如果您想要超越一次写入一次读取的真正双向通信,您还需要注意缓冲。如果您阅读this,您会有所了解,但您应该知道,缓冲 IO 在基于 UNIX 的系统中几乎总是这样工作的——这不是 Python 的怪癖。运行man stdio.h了解更多信息。

    【讨论】:

    • 我会检查一下交流,然后,希望它能按我的意愿工作。感谢您提供的管道信息,我一定会阅读的。
    【解决方案2】:

    您正在要求程序 1 从程序 2 读取输入。您正在暂停程序 2 5 秒钟,然后它才会输出任何内容。显然,程序 1 需要等待那五秒钟。所以发生的事情是完全可以预料的。

    在读取其输出之前是否必须等待其子进程终止?

    在某种程度上,是的,因为输入和输出是缓冲的,所以即使您将延迟移动到之后,您打印的结果也会发生相同的情况。

    raw_input() 在任何情况下都会等待换行。

    【讨论】:

    • 我不太明白。在暂停之前,我已经在第二个程序中输出了 2 行...但是关于缓冲的那句话有点吓人...
    • 我的答案涵盖了这种“大体上”的行为,但是如果您要调用 readline() 或 read(nbytes),这个答案仍然是相关的 - 默认情况下缓冲 Python STDIN 和 STDOUT,并且在您调用 flush() 或文件描述符关闭(通常在程序存在时)之前不会刷新。
    • @Yannbane:是的,但是前两行是在您等待第一个程序的输入之前发送的!我不明白你期望发生什么,所以我不知道如何调整旅游期望。
    • 感谢所有信息,但我最终改用了临时文件。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-20
    • 2014-05-28
    • 1970-01-01
    • 1970-01-01
    • 2011-01-20
    相关资源
    最近更新 更多