运行shell命令并捕获输出

  • 问题:
  • 我想编写一个函数来执行一个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但是对于较新的版本,它不再是推荐的方法

    如果使用的是python3.5或更高版本,并且不需要向后兼容,那么new run function是推荐的。它为子流程模块提供了一个非常通用的高级API。要捕获程序的输出,请传递子流程.管道标记到stdout关键字参数。然后访问返回的https://docs.python.org/3/library/subprocess.html\subprocess.CompletedProcess“rel=”noreferrer“>完成过程对象:

    >>> 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']
    >>> input = 'foo\nfoofoo\n'.encode('utf-8')
    >>> result = subprocess.run(cmd, stdout=subprocess.PIPE, input=input)
    >>> result.stdout.decode('utf-8')
    'foofoo\n'

    您可以通过传递stderr来捕获错误=子流程.管道(捕获到结果.stderr)或stderr=子流程.STDOUT(捕获到结果.stdout以及常规输出)。如果不考虑安全性,还可以通过传递shell=True来运行更复杂的shell命令,如下面的注释所述

    与传统的做事方式相比,这只增加了一点复杂性。但我认为这是值得的:现在您可以单独使用run函数完成几乎任何需要做的事情

    如果您使用的是旧版本的Python,或者需要适度的向后兼容性,那么您可以使用上面简要描述的check_output函数。它从python2.7开始就可以使用了

    subprocess.check_output(*popenargs, **kwargs)  

    它接受与Popen相同的参数(见下文),并返回一个包含程序输出的字符串。这个答案的开头有一个更详细的用法示例。在Python3.5及更高版本中,check_output相当于使用check=Truestdout=PIPE执行run,并只返回stdout属性。在

    你可以通过stderr=子流程.STDOUT以确保返回的输出中包含错误消息——但在某些版本的Python中传递stderr=子流程.管道检查输出可能导致deadlocks. 如果不考虑安全性,还可以通过传递shell=True来运行更复杂的shell命令,如下面的注释所述

    如果需要从stderr进行管道连接或将输入传递到流程,check\u output将无法完成任务。在这种情况下,请参见下面的Popen示例。在

    如果您需要深入的向后兼容性,或者您需要比check\u output提供的更复杂的功能,则必须直接使用Popen对象,这些对象为子进程封装了底层API。在

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

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

    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=PIPEcommunication还允许您通过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)才能使通信工作

    在某些罕见的情况下,您可能需要复杂的实时输出捕获。Vartec的答案表明了一条前进的道路,但是如果不小心使用,除了通信之外的方法很容易出现死锁

    与上述所有函数一样,当不考虑安全性时,可以通过传递shell=True来运行更复杂的shell命令

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

    通常,对运行检查输出Popen构造函数的每个调用都执行一个单个程序。这意味着没有华丽的狂欢风格的管道。如果要运行复杂的shell命令,可以传递shell=True,这三个函数都支持它

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

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

    或者

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

    直接连接管道的诱惑是强烈的;抵制它。否则,你很可能会看到死锁或者不得不做一些像this

    2.Unicode considerations

    check_output在python2中返回一个字符串,而在python3中返回一个bytes对象。值得花点时间learn about unicode如果你还没有