运行shell命令并捕获输出

2024-04-25 21:57:13 发布

您现在位置:Python中文网/ 问答频道 /正文

我想编写一个函数,它将执行一个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'

Tags: 函数run字符串代码命令行test命令消息
3条回答

这个问题的答案取决于您使用的Python版本。最简单的方法是使用^{}函数:

>>> 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

如果您使用的是Python 3.5或更高版本,并且不需要向后兼容,建议使用new ^{} function。它为subprocess模块提供了一个非常通用的高级API。要捕获程序的输出,请将subprocess.PIPE标志传递给stdout关键字参数。然后访问返回的^{}对象的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对象,因此如果您想要一个合适的字符串,就需要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(捕获到result.stderr)或stderr=subprocess.STDOUT(捕获到result.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相当于使用check=Truestdout=PIPE执行run,并只返回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构造函数接受不带参数的单个命令,或接受包含命令的列表作为其第一项,后跟任意数量的参数,作为列表中的单独项。^{}可以帮助将字符串解析为格式适当的列表。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命令。

注释

1。运行shell命令:参数shell=True

通常,对runcheck_outputPopen构造函数的每个调用都执行一个单个程序。这意味着没有花哨的bash风格的管道。如果要运行复杂的shell命令,可以传递所有三个函数都支持的shell=True

但是,这样做会提高security concerns。如果您所做的不仅仅是简单的脚本编写,那么最好分别调用每个进程,并将每个进程的输出作为输入传递给下一个进程,通过

run(cmd, [stdout=etc...], input=other_output)

或者

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

直接连接管道的诱惑力很强;抵制住它。否则,您可能会看到死锁,或者不得不做一些像this这样的讨厌的事情。

2。Unicode注意事项

check_output在Python 2中返回一个字符串,但在Python 3中返回一个bytes对象。如果你还没有的话,花点时间learn about unicode是值得的。

像这样的:

def runProcess(exe):    
    p = subprocess.Popen(exe, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    while(True):
        # returns None while subprocess is running
        retcode = p.poll() 
        line = p.stdout.readline()
        yield line
        if retcode is not None:
            break

注意,我正在将stderr重定向到stdout,这可能不是您想要的,但我也需要错误消息。

此函数在它们出现时逐行生成(通常您必须等待子流程完成才能获得完整的输出)。

对于您的情况,用法是:

for line in runProcess('mysqladmin create test -uroot -pmysqladmin12'.split()):
    print line,

这要简单得多,但只适用于Unix(包括Cygwin)和Python2.7。

import commands
print commands.getstatusoutput('wc -l file')

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

对于同时在Python2和Python3中工作的解决方案,请改用subprocess模块:

from subprocess import Popen, PIPE
output = Popen(["date"],stdout=PIPE)
response = output.communicate()
print response

相关问题 更多 >