【问题标题】:How to send STDIN twice to Popen process, each time with EOF?如何将 STDIN 两次发送到 Popen 进程,每次都使用 EOF?
【发布时间】:2021-04-03 17:03:49
【问题描述】:

我有这部分代码:

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

写入字符串stdin 来处理p 的STDIN。

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

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

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

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

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

【问题讨论】:

  • 这是一个坏掉的设计。如果它需要输入两次,它应该接受两个文件名参数或其他东西。
  • 出于安全原因,它不能。输入是敏感的,不能接触磁盘。具体来说,第一个输入是密码,第二个输入是明文(要加密)。
  • 为什么它是一个损坏的设计呢?你让我很好奇。
  • 你试过p.stdin.write('\n')吗?
  • @LiorCohen - 不起作用。有什么方法可以发送 EOF?这是 Python 的限制吗?还是概念限制?

标签: python subprocess stdin popen eof


【解决方案1】:

EOF 不是字符,它只是意味着没有更多数据要读取。

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

【讨论】:

  • 我知道。没说是人物。为什么无法发送没有更多数据可读取的信息?
  • 从技术上讲,这应该是可能的,因为操作系统可以做到这一点 - 即当您执行 ctrl + D 时。我认为历史上没有必要将这样的功能添加到语言中。您可以尝试解决它,也许通过关闭p.stdin.close()?但可以肯定的是,您不能发送它。
  • 是的,关闭是有效的。但是,p.stdin关闭后如何重新打开呢?
  • 真的不知道,但你的兴趣已经达到顶峰,我会关注这个问题
【解决方案2】:

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

当我将subprocess.popen()stdin=subprocess.PIPE 一起使用时,我发现子进程在stdin.close() 发生或主进程退出之前无法获取任何内容,两者都发送EOF 信号但使PIPE 成为一次性的。当然我试过stdin.writelines()stdin.flush()pickle.dump()等,但都没有成功。

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

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

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

其实我觉得是file.write()的错。任何调用write() 方法的函数都不可避免地无法发送EOF,除非我们绕过write() 方法,而这在不使用NumPy 等C 扩展的情况下是不可能的。

现在通过 PIPE 发送一般数据有两种方式:

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

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

  2. 如果 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,我的调试终于结束了......


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

>>> 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

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

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

另外,subprocess.PIPE 似乎有一个最大缓冲区大小,因此传输渲染图像是不可能的。作为我的实验结果,将它们分成块并没有帮助。

【讨论】:

    猜你喜欢
    • 2015-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-04
    • 2017-10-20
    • 2018-04-28
    • 2013-07-20
    • 2018-11-06
    相关资源
    最近更新 更多