Python 中运行 shell 命令并捕获输出

Python exec shell 命令

本文分享几种Python中运行命令行并获取输出的方法

subprocess.check_output

在所有官方维护的 Python 版本中,最简单的方法是使用该subprocess.check_output函数:

>>> subprocess.check_output(['ls', '-l'])
b'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'Code language: JavaScript (javascript)

check_output运行一个只接受参数作为输入的程序。它返回的结果与打印到stdout的完全相同

该check_output函数适用于所有官方维护的 Python 版本。但是对于更新的版本,可以使用更灵活的方法。

现代版本的 Python(3.5 或更高版本):run

如果使用的是Python 3.5+,并且不需要向后兼容run,则推荐 subprocess。要捕获程序的输出,请将subprocess.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'Code language: JavaScript (javascript)

返回值是一个bytes对象,如果想要一个正确的字符串,你就需要decode。假设被调用的进程返回一个 UTF-8 编码的字符串:

>>> result.stdout.decode('utf-8')
'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'Code language: JavaScript (javascript)

如果需要,还可以全部压缩为单行:

>>> 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'Code language: JavaScript (javascript)

如果要将输入传递给进程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'Code language: JavaScript (javascript)

可以通过传递stderr=subprocess.PIPE(输出到result.stderr) 或 stderr=subprocess.STDOUT(输出到stdout) 来捕获错误。如果想run在进程返回非零退出码时抛出异常,可以设置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'Code language: PHP (php)

旧版本的 Python (3-3.4):更多关于check_output

如果使用的是旧版本的 Python,或者需要适度的向后兼容性,可以使用check_output

subprocess.check_output(*popenargs, **kwargs) Code language: CSS (css)

它采用与Popen(见下文)相同的参数,并返回一个包含程序输出的字符串。在 Python 3.5+ 中,check_output相当于run使用check=Truestdout=PIPE,并且只返回stdout属性。

可以通过stderr=subprocess.STDOUT以确保错误消息包含在返回的输出中。

如果需要从stderr流程传输或将输入传递给流程,check_output则无法完成任务。在这种情况下,请参阅Popen下面的示例。

复杂的应用程序和 Python 的旧版本(2.6 及以下):Popen

如果需要深度向后兼容性,或者如果需要比check_output或run提供更复杂的功能,则必须直接使用Popen对象,这些对象封装了子流程的低级 API。

Popen构造函数接受一个不带参数的命令,或者一个包含命令的列表作为其第一项,然后是任意数量的参数,每个参数作为列表中的一个单独项。shlex.split可以帮助将字符串解析为适当格式的列表。Popen对象还接受用于进程 IO 管理和低级配置的许多不同参数。

发送输入和捕获输出,communicate几乎总是首选方法。如:

output = subprocess.Popen(["mycmd", "myarg"], 
                          stdout=subprocess.PIPE).communicate()[0]Code language: JavaScript (javascript)

或者

>>> import subprocess
>>> p = subprocess.Popen(['ls', '-a'], stdout=subprocess.PIPE, 
...                                    stderr=subprocess.PIPE)
>>> out, err = p.communicate()
>>> print out
.
..
fooCode language: JavaScript (javascript)

如果设置stdin=PIPE,communicate还允许通过以下方式将数据传递给进程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
foofooCode language: PHP (php)

commands

下面的方法仅适用于 Unix(包括 Cygwin)和 Python2.7。

import commands
print commands.getstatusoutput('wc -l file')Code language: JavaScript (javascript)

它返回一个带有 (return_value, output) 的元组。

发表评论