Python 子进程:打印到标准输入,读取标准输出直到换行,重复

Python subprocess: Print to stdin, read stdout until newline, repeat

我希望使用 Python 3.5 与交互式命令行应用程序进行交互。我的想法是,我在 Python 脚本的开头启动该过程并保持打开状态。在一个循环中,我打印一个文件路径,然后是一行 return,到 stdin,在处理过程中等待四分之一秒左右,然后从 stdout 读取直到它到达一个换行符。

这与 subprocesscommunicate 功能非常相似,但我正在等待一行 return 而不是等待进程终止。有人知道一种相对简单的方法吗?

编辑:如果可能的话,最好使用标准库来执行此操作,而不是 pexpect 等第三方库。

您可以为此使用 subprocess.Popen。

像这样:

proc = subprocess.Popen(['my-command'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)

现在 proc.stdinproc.stdout 是将数据发送到子进程标准输入并从子进程标准输出读取的管道的末端。

由于您只对阅读以换行符结尾的行感兴趣,因此您可能可以解决缓冲引起的任何问题。缓冲是使用子流程与交互式流程进行通信时的一大陷阱。通常 I/O 是行缓冲的,这意味着如果子进程不以换行符终止一行,您可能永远不会在 proc.stdout 上看到任何数据,反之亦然,您写入 proc.stdin - 如果您不以换行符结尾,它可能看不到它。您可以关闭缓冲,但这不是那么简单,而且与平台无关。

您可能需要解决的另一个问题是您无法确定子进程是在等待输入还是已经向您发送输出,除非通过管道写入和读取。因此,您可能需要启动第二个线程,以便您可以等待 proc.stdout 上的输出并同时写入 proc.stdin 而不会 运行 陷入死锁,因为两个进程都阻塞在管道 I/O(或者,如果您使用的 Unix 支持 select 和文件句柄,请使用 select 确定哪些管道已准备好接收或读取)。

这听起来像是事件循环的工作。 subprocess 模块开始显示其在复杂任务下的压力。

我用 Twisted 完成了这个,方法是对以下内容进行子类化:

twisted.internet.endpoints.ProcessEndpoint
twisted.protocols.basic.LineOnlyReceiver

大多数 Twisted 文档都使用套接字作为端点,但调整进程代码并不难。