【问题标题】:Python Popen, closing streams and multiple processesPython Popen,关闭流和多个进程
【发布时间】:2023-10-22 10:27:01
【问题描述】:

我有一些数据想要 gzip、uuencode 然后打印到标准输出。我基本上拥有的是:

compressor = Popen("gzip", stdin = subprocess.PIPE, stdout = subprocess.PIPE)
encoder    = Popen(["uuencode", "dummy"], stdin = compressor.stdout)

我向压缩器提供数据的方式是通过compressor.stdin.write(stuff)。

我真正需要做的是向压缩器发送 EOF,我不知道该怎么做。

在某些时候,我尝试了compressor.stdin.close() 但这不起作用——当压缩器直接写入文件时它运行良好,但在上述情况下,进程不会终止并停止在compressor.wait()上。

建议?在这种情况下,gzip 就是一个示例,我确实需要做一些事情,将一个进程的输出通过管道传输到另一个进程。

注意:我需要压缩的数据不适合内存,所以在这里交流并不是一个好的选择。另外,如果我只是运行

compressor.communicate("Testing") 

在上面两行之后,它仍然挂起错误

文件“/usr/lib/python2.4/subprocess.py”,第 1041 行,在通信中 rlist, wlist, xlist = select.select(read_set, write_set, [])

【问题讨论】:

  • 虽然 cobbal 的答案适用于 gzip,但这实际上只是一个示例——我确实需要管道 ...

标签: python stream popen eof


【解决方案1】:

我怀疑问题出在您打开管道的顺序上。 UUEncode 有趣的是,如果没有以正确的方式传入管道,它会在您启动它时发出呜呜声(尝试在 Popen 调用中自行启动该死的东西,以查看仅使用 PIPE 作为标准输入和标准输出的爆炸)

试试这个:

encoder = Popen(["uuencode", "dummy"], stdin=PIPE, stdout=PIPE)
compressor = Popen("gzip", stdin=PIPE, stdout=encoder.stdin)

compressor.communicate("UUencode me please")
encoded_text = encoder.communicate()[0]
print encoded_text

begin 644 dummy
F'XL(`%]^L$D``PL-3<U+SD])5<A-52C(24TL3@4`;2O+"!(`````
`
end

你是对的,顺便说一句...没有办法将通用 EOF 发送到管道中。毕竟,每个程序确实定义了自己的 EOF。这样做的方法是关闭管道,就像您尝试做的那样。

编辑:我应该更清楚地了解 uuencode。作为一个 shell 程序,它的默认行为是期待控制台输入。如果您在没有“实时”传入管道的情况下运行它,它将阻止等待控制台输入。通过第二次打开编码器,在您将材料发送到压缩机管道之前,编码器正在阻塞等待您开始输入。 Jerub 是对的,因为有什么东西阻塞了。

【讨论】:

  • 不幸的是,它仍然需要将整个输出存储在内存中,如果您尝试读取()编码器的标准输出,它似乎仍然挂起
  • 使用通信(),虽然你不应该阅读标准输出。 stdout 内容以元组形式返回,带有来自通信命令的 stdin。您可以将其更改为在两个管道上使用写入和读取,但请参阅来自monkut 的评论,python 文档建议不要这样做。
  • 如果你有一些庞大的数据,也许使用临时文件在shell进程之间进行通信是最好的?
  • 好点,或者您可以从 uuencode man 5 页面和 binascii.a2b_uu(string) 中破解您自己的实现
  • 我支持这个概念,但也许您真正想要的是使用 shell=True 并直接在 shell 命令上从 gzip 管道到 uuencode。
【解决方案2】:

这不是你应该直接在 python 中做的事情,关于事情的工作方式有一些古怪之处,这使得用 shell 来做这件事是一个更好的主意。如果你可以只使用 subprocess.Popen("foo | bar", shell=True),那就更好了。

可能发生的情况是 gzip 还不能输出其所有输入,并且该进程在其 stdout 写入完成之前不会退出。

如果您使用 strace,您可以查看进程正在阻塞的系统调用。使用ps auxwf 发现哪个进程是gzip 进程,然后使用strace -p $pidnum 查看它正在执行什么系统调用。请注意,stdin 是 FD 0,stdout 是 FD 1,您可能会看到它在这些文件描述符上读取或写入。

【讨论】:

    【解决方案3】:

    如果您只想压缩而不需要文件包装器,请考虑使用 zlib 模块

    import zlib
    compressed = zlib.compress("text")
    

    shell=True 和 unix 管道建议不起作用的任何原因?

    from subprocess import *
    
    pipes = Popen("gzip | uuencode dummy", stdin=PIPE, stdout=PIPE, shell=True)
    for i in range(1, 100):
        pipes.stdin.write("some data")
    pipes.stdin.close()
    print pipes.stdout.read()
    

    似乎有效

    【讨论】: