在所有官方维护的Python版本中,最简单的方法是使用子进程。check_output功能:
>>> subprocess.check_output(['ls', '-l'])
b'total 0\n-rw-r--r-- 1 memyself staff 0 Mar 14 11:04 files\n'
Check_output运行一个单独的程序,只接受参数作为输入它返回打印到stdout的结果。如果您需要向stdin写入输入,请跳过运行或Popen部分。如果您想执行复杂的shell命令,请参阅答案末尾shell=True的说明。
check_output函数适用于所有官方维护的Python版本。但是对于最近的版本,可以使用更灵活的方法。
Python(3.5或更高版本)的现代版本:运行
如果您使用的是Python 3.5+,并且不需要向后兼容,那么对于大多数任务,官方文档都推荐使用新的run函数。它为子流程模块提供了一个非常通用的高级API。要捕获程序的输出,请传递子流程。PIPE标志指向stdout关键字参数。然后访问返回的CompletedProcess对象的stdout属性:
>>> import subprocess
>>> result = subprocess.run(['ls', '-l'], stdout=subprocess.PIPE)
>>> result.stdout
b'total 0\n-rw-r--r-- 1 memyself staff 0 Mar 14 11:04 files\n'
返回值是一个bytes对象,所以如果想要一个合适的字符串,就需要解码它。假设被调用进程返回一个utf -8编码的字符串:
>>> result.stdout.decode('utf-8')
'total 0\n-rw-r--r-- 1 memyself staff 0 Mar 14 11:04 files\n'
如果需要,这些都可以压缩成一行代码:
>>> subprocess.run(['ls', '-l'], stdout=subprocess.PIPE).stdout.decode('utf-8')
'total 0\n-rw-r--r-- 1 memyself staff 0 Mar 14 11:04 files\n'
如果你想把输入传递给进程的stdin,你可以把一个bytes对象传递给input关键字参数:
>>> cmd = ['awk', 'length($0) > 5']
>>> ip = 'foo\nfoofoo\n'.encode('utf-8')
>>> result = subprocess.run(cmd, stdout=subprocess.PIPE, input=ip)
>>> result.stdout.decode('utf-8')
'foofoo\n'
你可以通过传递stderr=subprocess来捕捉错误。PIPE(捕获到result.stderr)或stderr=subprocess。捕获结果。标准输出和常规输出)。如果希望在流程返回非零退出码时运行抛出异常,可以传递check=True。(或者你可以检查上面result的returncode属性。)当安全性不是问题时,您还可以通过传递shell=True来运行更复杂的shell命令,如答案末尾所述。
Python的后续版本进一步简化了上述内容。在Python 3.7+中,上面的一行代码可以这样拼写:
>>> subprocess.run(['ls', '-l'], capture_output=True, text=True).stdout
'total 0\n-rw-r--r-- 1 memyself staff 0 Mar 14 11:04 files\n'
与旧的操作方式相比,以这种方式使用run只增加了一点复杂性。但是现在您可以单独使用run函数完成几乎所有需要做的事情。
旧版本的Python(3-3.4):更多关于check_output
如果您使用的是较旧版本的Python,或者需要适度的向后兼容性,您可以使用上面简要描述的check_output函数。它从Python 2.7开始就可用了。
subprocess.check_output(*popenargs, **kwargs)
它接受与Popen相同的参数(见下文),并返回包含程序输出的字符串。这个回答的开头有一个更详细的用法示例。在Python 3.5+中,check_output相当于执行带有check=True和stdout=PIPE的run,并且只返回stdout属性。
你可以传递stderr=subprocess。STDOUT以确保返回的输出中包含错误消息。当安全性不是问题时,您还可以通过传递shell=True来运行更复杂的shell命令,如答案末尾所述。
如果您需要从stderr管道或将输入传递给流程,check_output将无法完成该任务。在这种情况下,请参阅下面的Popen示例。
复杂应用程序和Python(2.6及以下)的遗留版本:Popen
如果需要深度向后兼容性,或者需要比check_output或运行provider更复杂的功能,则必须直接使用Popen对象,它为子进程封装了低级API。
Popen构造函数可以接受不带参数的单个命令,也可以接受包含命令作为第一项的列表,后面跟着任意数量的参数,每个参数作为列表中的单独项。shlex。Split可以帮助将字符串解析为适当格式化的列表。Popen对象还接受许多用于进程IO管理和低级配置的不同参数。
要发送输入和捕获输出,通信几乎总是首选的方法。如:
output = subprocess.Popen(["mycmd", "myarg"],
stdout=subprocess.PIPE).communicate()[0]
Or
>>> import subprocess
>>> p = subprocess.Popen(['ls', '-a'], stdout=subprocess.PIPE,
... stderr=subprocess.PIPE)
>>> out, err = p.communicate()
>>> print out
.
..
foo
如果你设置了stdin=PIPE, communication也允许你通过stdin向进程传递数据:
>>> cmd = ['awk', 'length($0) > 5']
>>> p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
... stderr=subprocess.PIPE,
... stdin=subprocess.PIPE)
>>> out, err = p.communicate('foo\nfoofoo\n')
>>> print out
foofoo
请注意Aaron Hall的回答,这表明在某些系统上,您可能需要将stdout、stderr和stdin全部设置为PIPE(或DEVNULL)才能使通信正常工作。
在极少数情况下,您可能需要复杂的实时输出捕获。Vartec的回答提出了一条前进的道路,但如果不小心使用,除了沟通之外的其他方法很容易陷入僵局。
与上述所有函数一样,当安全性不是问题时,您可以通过传递shell=True来运行更复杂的shell命令。
笔记
1. 执行shell命令:参数shell=True
通常,对run、check_output或Popen构造函数的每次调用都执行一个程序。这意味着没有花哨的敲击式管道。如果你想运行复杂的shell命令,你可以传递shell=True,这三个函数都支持。例如:
>>> subprocess.check_output('cat books/* | wc', shell=True, text=True)
' 1299377 17005208 101299376\n'
然而,这样做会引起安全问题。如果您要做的不是简单的脚本,那么最好分别调用每个进程,并将每个进程的输出作为输入传递给下一个进程,即via
run(cmd, [stdout=etc...], input=other_output)
Or
Popen(cmd, [stdout=etc...]).communicate(other_output)
直接连接管道的诱惑很强烈;抵制它。否则,您可能会看到死锁,或者不得不做类似这样的讨厌的事情。