如何写入Python子进程'stdin?

jon*_*opf 55 python stdin subprocess nuke

我正在尝试编写一个启动子进程的Python脚本,并写入子进程stdin.我还希望能够确定子进程崩溃时要采取的操作.

我正在尝试启动的过程是一个程序nuke,它有自己的内置版本的Python,我希望能够提交命令,然后告诉它在执行命令后退出.到目前为止,我已经知道,如果我在命令提示符上启动Python,然后nuke作为子进程启动,那么我可以输入命令nuke,但我希望能够将所有这些都放在一个脚本中,以便主Python程序可以启动nuke然后写入其标准输入(因此也可以写入其内置版本的Python),并告诉它做一些时髦的东西,所以我编写了一个脚本,nuke如下所示:

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

然后没有任何事情发生,因为nuke等待用户输入.我现在如何写入标准输入?

我这样做是因为我正在运行一个插件nuke,导致它在渲染多个帧时间歇性地崩溃.所以我希望这个脚本能够启动nuke,告诉它做某事然后如果它崩溃了,再试一次.因此,如果有办法赶上崩溃并且仍然可以,那就太棒了.

jro*_*jro 74

使用它可能更好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]
Run Code Online (Sandbox Code Playgroud)

"更好",因为这个警告:

使用communic()而不是.stdin.write,.stdout.read或.stderr.read来避免由于任何其他OS管道缓冲区填满和阻塞子进程而导致的死锁.

  • 对于python 3.4,您需要执行`p.communicate(input =“输入数据” .encode())` (4认同)
  • `communic`方法读取数据直到收到EOF.如果要动态地与进程交互,请使用`p.stdin.write('data')`访问管道.有关阅读,请参阅我之前的评论.警告是关于这种通信方式,所以请注意不要填充缓冲区.最简单的验证方法就是尝试... (2认同)
  • 当您在寻找上述问题的答案时,例如,如果您需要执行“ ping”命令而“ communicate”没有坚持,那么该答案将毫无帮助。 (2认同)

L0t*_*tad 20

subprocess3.5开始,有了该subprocess.run()函数,它提供了一种方便的方式来初始化对象并与Popen()对象交互。run()接受一个可选input参数,通过它您可以将东西传递给stdin(就像您使用 一样Popen.communicate(),但是一次性完成)。

调整jro的示例来使用run()如下所示:

import subprocess
p = subprocess.run(['myapp'], input='data_to_write', capture_output=True, text=True)
Run Code Online (Sandbox Code Playgroud)

执行后,p将是一个CompletedProcess对象。通过设置capture_outputto True,我们可以使用一个p.stdout属性,如果我们关心它的话,它可以让我们访问输出。text=True告诉它使用常规字符串而不是字节。如果需要,您还可以添加参数,check=True使其在退出状态(无论通过 均可访问p.returncode)不为 0 时抛出错误。

这是“现代”/快速且简单的方法。

  • 请注意 `text` 最近才出现,为了与旧版本兼容,您可能需要 `input='data_to_write'.encode('utf-8')` 代替 (2认同)

eay*_*din 8

澄清几点:

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

然而,在stdin使用subprocess.communicatewith 时input,您需要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]
Run Code Online (Sandbox Code Playgroud)

我特意留下了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()
Run Code Online (Sandbox Code Playgroud)

这与这里的响应有些不同,在那里你直接管道两个进程而不直接在 Python 中添加数据。

希望能帮助别人。