如何将 STDIN 发送到 Popen 进程两次,每次都使用 EOF?

How to send STDIN twice to Popen process, each time with EOF?

提问人:caveman 提问时间:12/26/2020 更新时间:7/6/2021 访问量:504

问:

我有这部分代码:

for stdin in stdins:
    p.stdin.write(stdin)

它将字符串写入进程的 STDIN。stdinp

挑战在于:该过程希望在进入下一个 STDIN 之前看到 EOF。p

对于上面的循环,问题在于进程将后续视为第一个 STDIN 输入集合的输入。因为,如前所述,希望在移动到后续字段之前看到 EOF。p.stdin.write(stdin)pp

所以,我的问题是:如何在Python中解决这个问题?该过程需要看到如下内容:

for stdin in stdins:
    p.stdin.write(stdin)
    p.stdin.send_eof()

约束:解决方案不得使用 pexpect。

Python subprocess stdin popen eof

评论

0赞 tripleee 12/26/2020
这是一个破碎的设计。如果它需要两次输入,它应该需要两个文件名参数或其他参数。
0赞 caveman 12/26/2020
出于安全原因,它不能。输入是敏感的,不得接触磁盘。具体来说,第一个输入是密码,第二个输入是纯文本(要加密)。
0赞 caveman 12/26/2020
但是,为什么它的设计会损坏呢?你让我很好奇。
0赞 Lior Cohen 12/26/2020
你试过吗?p.stdin.write('\n')
0赞 caveman 12/26/2020
@LiorCohen - 行不通。有什么方法可以发送EOF吗?这是 Python 限制吗?还是概念限制?

答:

0赞 pishpish 12/26/2020 #1

EOF不是一个字符,它只是意味着没有更多的数据可以读取。

因此,我不相信你所追求的东西在 Python 或大多数其他语言中是可能的。

评论

0赞 caveman 12/26/2020
我知道。没说是角色。为什么无法发送没有更多数据可读取的信息?
0赞 pishpish 12/26/2020
从技术上讲,这应该是可能的,因为操作系统可以做到这一点 - 即当你这样做时.我认为从历史上看,没有必要为语言添加这样的功能。您可以尝试解决它,也许通过关闭?但可以肯定的是,你不能发送它。ctrl + Dp.stdin.close()
0赞 caveman 12/26/2020
是的,关闭是有效的。但是,关闭后如何重新打开?p.stdin
0赞 pishpish 12/26/2020
真的不知道,但你引起了我的兴趣,我会关注这个问题
0赞 Seniorious 7/4/2021 #2

当我尝试在python中用几个子进程进行异步渲染时,我遇到了同样的问题,这些子进程需要以低延迟与主进程通信。

当我使用 时,我发现子进程在发生或主进程退出之前无法获得任何内容,既发送 EOF 信号,又使 PIPE 成为一次性的。当然,我试过了,等等,但没有一个奏效。subprocess.popen()stdin=subprocess.PIPEstdin.close()stdin.writelines()stdin.flush()pickle.dump()

但是有一种方法可以使用 NumPy 与子进程重复通信。

ndarray.tofile 可以直接将数组发送到 File 对象。尽管文档声明它等同于 file.write(a.tobytes()),但它确实有意义。我很困惑,直到我在文档页面的末尾读到这个:

当 fid 是文件对象时,数组内容将直接写入文件,绕过文件对象的 write 方法。因此,tofile 不能与支持压缩的文件对象(例如 GzipFile)或不支持 fileno() 的类文件对象(例如 BytesIO)一起使用。

其实,我认为这是我的错。任何带有调用方法的函数都不可避免地无法发送 EOF,除非我们绕过该方法,如果不使用一些 C 扩展(如 NumPy)是不可能的。file.write()write()write()

为了通过PIPE发送常规数据,现在有两种方式:

  1. NumPy 支持 ,这意味着您可以直接将消息打包到对象数组中。参见 numpy.lib.formatdtype=object

    存储对象数组,即包含任意 Python 对象元素的数组。包含对象数组的文件不可映射,但可以读取和写入磁盘。

  2. 如果 Struct 具有常规模式,您可以将 Struct 声明为 dtype 来打包您的消息,这就是我的情况。这是我的例子。

    task = np.dtype([(  "index",  np.uint8         ),
                     (   "text",  np.unicode_, 128 ),
                     (  "color",  np.uint8,    2   ),
                     (   "size",  np.uint8         )])
    for i in range(123):
        np.empty(1, dtype=task).tofile(s.stdin)  # s is the subprocess' name.
        time.sleep(1)
    

    然后我成功地在子进程中分别获得了 123 次消息。

    我真的希望这能帮助到你。因为我花了将近 4 天的时间才找到这个解决方案。我几乎正在考虑使用磁盘上的真实文件来完成进程之间的通信 - 这应该更慢 - 但多亏了 NumPy,我的调试终于结束了......


另外,我认为发送 EOF 没有意义。您可以在 python 控制台中尝试此操作。np.save()

>>> import numpy as np
>>> import sys
>>> a = np.arange(100).reshape(10,10)
>>> a.tofile(sys.stdout.buffer)
... some garbled characters ...
>>> a.tofiler(sys.stdout)
... some garbled characters ...
>>> np.save(sys.stdout.buffer, a)
... some garbled characters ...
>>> np.save(sys.stdout, a)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<__array_function__ internals>", line 5, in save
  ...
TypeError: write() argument must be str, not bytes

原因是 accept bytes 参数而 accept str.因此,使用 array.tofile 写入不会导致任何错误,表明它没有调用方法。这引发了一个问题,它似乎不支持模式。对此感到抱歉。也许通过管道通过进程传输动态类型数据真的很难,但我听说在模块内部有一些方法可以在进程之间共享 RAM,这可能会有所帮助。sys.stdout.buffer.write()sys.stdout.write()sys.stdoutwrite()np.save()np.fromfiledtype=objectctype

提到我未能在终端(io.UnsupportedOperation: seek),但它在 PyCharm 的 python 控制台中运行良好。我对此一无所知。也许 PyCharm 的 python 控制台实际上也有 sys.stdin 的代理。

另外,它似乎是子过程。PIPE 具有最大缓冲区大小,因此无法传输渲染的图像。作为我的实验结果,将它们分成块无济于事。