我想编写一个函数,它将执行一个shell命令并将其输出作为字符串返回,不管是错误还是成功消息。我只想得到和命令行一样的结果。
什么样的代码示例可以做到这一点?
例如:

def run_command(cmd):
    # ??????

print run_command('mysqladmin create test -uroot -pmysqladmin12')
# Should output something like:
# mysqladmin: CREATE DATABASE failed; error: 'Can't create database 'test'; database exists'

最佳答案

这个问题的答案取决于您使用的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'

check_output运行一个只接受参数作为输入的程序。1它返回与stdout完全相同的结果。如果需要将输入写入stdin,请跳到runPopen部分。如果您想执行复杂的shell命令,请参阅本答案末尾的shell=True注释。
check_output函数适用于几乎所有仍在广泛使用的Python版本(2.7+)。2但对于较新版本,它不再是推荐的方法。
Python的现代版本(3.5或更高版本):run
如果您使用的是Python3.5或更高版本,并且不需要向后兼容,建议使用new run function。它为subprocess模块提供了一个非常通用的高级API。要捕获程序的输出,请将subprocess.PIPE标志传递给stdout关键字参数。然后访问返回的stdout对象的CompletedProcess属性:
>>> 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对象,因此如果您想要一个合适的字符串,就需要decode它。假设调用的进程返回一个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']
>>> input = 'foo\nfoofoo\n'.encode('utf-8')
>>> result = subprocess.run(cmd, stdout=subprocess.PIPE, input=input)
>>> result.stdout.decode('utf-8')
'foofoo\n'

您可以通过传递stderr=subprocess.PIPE(capture toresult.stderr)或stderr=subprocess.STDOUT(capture toresult.stdout和常规输出)来捕获错误。当不考虑安全性时,也可以通过传递shell=True来运行更复杂的shell命令,如下面的注释所述。
与旧的做事方式相比,这增加了一些复杂性。但我认为这是值得的回报:现在你几乎可以用run函数做任何需要做的事情。
旧版本的Python(2.7-3.4):check_output
如果您使用的是较旧版本的Python,或者需要适度的向后兼容性,那么您可以使用上面简要描述的check_output函数。它从Python2.7开始就可用了。
subprocess.check_output(*popenargs, **kwargs)

它接受与Popen相同的参数(见下文),并返回包含程序输出的字符串。这个答案的开头有一个更详细的用法示例。在Python 3.5及更高版本中,check_output相当于使用runcheck=True执行stdout=PIPE,并只返回stdout属性。
可以传递stderr=subprocess.STDOUT以确保返回的输出中包含错误消息,但在某些版本的Python中,传递stderr=subprocess.PIPEcheck_output可能会导致deadlocks。当不考虑安全性时,也可以通过传递shell=True来运行更复杂的shell命令,如下面的注释所述。
如果您需要通过管道从stderr传递输入到进程,check_output将无法完成任务。在这种情况下,请参见下面的Popen示例。
复杂的应用程序和Python的遗留版本(2.6及以下版本):Popen
如果您需要深入的向后兼容性,或者如果您需要比check_output提供的功能更复杂的功能,则必须直接使用Popen对象,这些对象封装了子流程的低级API。
Popen构造函数接受没有参数的单个命令,或者接受包含命令的列表作为其第一项,后跟任意数量的参数,每个参数都作为列表中的单独项。shlex.split可以帮助将字符串解析为适当格式的列表。Popen对象还接受host of different arguments用于进程IO管理和低级配置。
要发送输入和捕获输出,communicate几乎总是首选方法。如所示:
output = subprocess.Popen(["mycmd", "myarg"],
                          stdout=subprocess.PIPE).communicate()[0]

或者
>>> import subprocess
>>> p = subprocess.Popen(['ls', '-a'], stdout=subprocess.PIPE,
...                                    stderr=subprocess.PIPE)
>>> out, err = p.communicate()
>>> print out
.
..
foo

如果设置了stdin=PIPEcommunicate还允许您通过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's answer,这表示在某些系统上,可能需要将stdoutstderrstdin全部设置为PIPE(或DEVNULL)才能使communicate完全工作。
在一些罕见的情况下,您可能需要复杂的实时输出捕获。Vartec的答案提出了一个前进的方向,但是如果不小心使用,除了communicate之外的方法很容易出现死锁。
与所有上述函数一样,当不考虑安全性时,可以通过传递shell=True来运行更复杂的shell命令。
笔记
一。运行shell命令:参数
通常,对shell=Trueruncheck_output构造函数的每个调用都执行一个程序。这意味着没有花哨的bash风格的管道。如果要运行复杂的shell命令,可以传递Popen,这三个函数都支持。
但是,这样做会提高security concerns。如果您所做的不仅仅是简单的脚本编写,那么最好分别调用每个进程,并将每个进程的输出作为输入传递给下一个进程,通过
run(cmd, [stdout=etc...], input=other_output)

或者
Popen(cmd, [stdout=etc...]).communicate(other_output)

直接连接管道的诱惑力很强;抵制住它。否则,你可能会看到死锁,或者不得不做一些像this这样的讨厌的事情。
2。Unicode注意事项
shell=True在Python2中返回一个字符串,但在Python3中返回一个check_output对象。如果你还没有,花点时间learn about unicode是值得的。

09-10 14:44
查看更多