如何写入Python子进程的stdin?


69

我正在尝试编写一个Python脚本来启动一个子进程,并将其写入子进程stdin。我还希望能够确定子进程崩溃时要采取的措施。

我试图启动的过程是一个名为的程序nuke,它具有自己的Python内置版本,我希望能够向其提交命令,然后告诉其在命令执行后退出。到目前为止,我已经得出结论,如果我在类似这样的命令提示符下启动Python,然后nuke作为子进程启动,那么我可以在中键入命令nuke,但是我希望能够将其全部放入脚本中,以便Python程序可以启动nuke,然后写入其标准输入(并因此写入其内置的Python版本),并使其执行令人眼花things乱的事情,因此,我编写了一个如下所示的脚本nuke

subprocess.call(["C:/Program Files/Nuke6.3v5/Nuke6.3", "-t", "E:/NukeTest/test.nk"])

然后什么也没有发生,因为nuke正在等待用户输入。我现在将如何写入标准输入?

我这样做是因为我正在运行一个带有nuke该插件的插件,导致它在渲染多个帧时间歇性崩溃。因此,我希望该脚本能够启动nuke,告诉它执行某些操作,然后如果崩溃,请重试。因此,如果有一种方法可以捕捉到崩溃并仍然可以正常运行,那就太好了。


1
如果您正在寻找快速编写一个字符串转换为子标准输入,使用inputsubprocess.run; 例如,subprocess.run(['cat'], input='foobar'.encode('utf-8'))
anishpatel

Answers:


89

最好使用communicate

from subprocess import Popen, PIPE, STDOUT
p = Popen(['myapp'], stdout=PIPE, stdin=PIPE, stderr=PIPE)
stdout_data = p.communicate(input='data_to_write')[0]

“更好”,因为此警告:

使用communication()而不是.stdin.write,.stdout.read或.stderr.read来避免死锁,这是由于其他任何OS管道缓冲区中的任何一个填满并阻塞了子进程而引起的。


aaah太好了,谢谢,同样,如果我只是执行导入子过程,我是否仍需要导入popen PIPE等?
jonathan topf 2011年

不,您不需要,但是您需要像这样引用它们subprocess.PIPE。此方法还将所有内容导入子流程模块中。在from subprocess import PIPE介绍到当前的命名空间,让你可以使用PIPE
jro 2011年

3
communicate方法读取数据,直到接收到EOF。如果要动态与流程交互,请使用访问管道p.stdin.write('data')。对于阅读,请参阅我以前的评论。警告是关于这种通信方式的,因此请注意不要填满缓冲区。验证这一点的最简单方法是尝试一下...
jro 2011年

6
对于Python 3.4,你需要做的p.communicate(input="data for input".encode())
QED

2
当您在寻找上述问题的答案时,例如,如果您需要执行“ ping”命令并​​且communicate不粘滞,则此答案完全无济于事。
UpmostScarab

6

要澄清一些要点:

正如jro提到的,正确的方法是使用subprocess.communicate

但是,在stdin使用subprocess.communicateinput,您需要stdin=subprocess.PIPE根据docs启动子过程。

请注意,如果要将数据发送到进程的stdin,则需要使用stdin = PIPE创建Popen对象。同样,要在结果元组中获得除None以外的任何内容,您还需要提供stdout = PIPE和/或stderr = PIPE。

此外QED在使用Python 3.4,您需要将字符串编码的评论所说,这意味着你需要字节传递给input,而不是一个string。这并非完全正确。根据文档,如果流以文本模式打开,则输入应为字符串(源是同一页)。

如果以文本模式打开流,则输入必须为字符串。否则,它必须是字节。

因此,如果未在文本模式下显式打开流,则应执行以下操作:

import subprocess
command = ['myapp', '--arg1', 'value_for_arg1']
p = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
output = p.communicate(input='some data'.encode())[0]

我特意将stderr上面的值STDOUT作为示例。

话虽这么说,有时您可能想要另一个进程的输出,而不是从头开始构建它。假设您要运行的等效项echo -n 'CATCH\nme' | grep -i catch | wc -m。通常,这应该返回“ CATCH”中的数字字符以及换行符,结果为6。此处的回显点是将CATCH\nme数据馈送到grep。因此,我们可以使用Python子wc进程链中的stdin作为变量将数据提供给grep,然后将stdout作为PIPE传递给进程的stdin(同时,摆脱多余的换行符):

import subprocess

what_to_catch = 'catch'
what_to_feed = 'CATCH\nme'

# We create the first subprocess, note that we need stdin=PIPE and stdout=PIPE
p1 = subprocess.Popen(['grep', '-i', what_to_catch], stdin=subprocess.PIPE, stdout=subprocess.PIPE)

# We immediately run the first subprocess and get the result
# Note that we encode the data, otherwise we'd get a TypeError
p1_out = p1.communicate(input=what_to_feed.encode())[0]

# Well the result includes an '\n' at the end, 
# if we want to get rid of it in a VERY hacky way
p1_out = p1_out.decode().strip().encode()

# We create the second subprocess, note that we need stdin=PIPE
p2 = subprocess.Popen(['wc', '-m'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)

# We run the second subprocess feeding it with the first subprocess' output.
# We decode the output to convert to a string
# We still have a '\n', so we strip that out
output = p2.communicate(input=p1_out)[0].decode().strip()

这与此处的响应有些不同,在此处您直接通过管道传输两个进程,而无需直接在Python中添加数据。

希望可以帮助某人。


-1

您可以为的stdin参数提供类似文件的对象subprocess.call()

该对象的文档Popen这里适用。

要捕获输出,您应该改为使用subprocess.check_output()带有类似参数的。从文档中:

>>> subprocess.check_output(
...     "ls non_existent_file; exit 0",
...     stderr=subprocess.STDOUT,
...     shell=True)
'ls: non_existent_file: No such file or directory\n'
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.