博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
运行shell命令并捕获输出
阅读量:2288 次
发布时间:2019-05-09

本文共 14311 字,大约阅读时间需要 47 分钟。

我想编写一个函数,该函数将执行命令并以字符串形式返回其输出,无论它是错误消息还是成功消息。 我只想获得与命令行相同的结果。

能做到这一点的代码示例是什么?

例如:

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'

#1楼

答案无法读取所有行,因此我制作了一个可以读取的版本:

def run_command(command):    p = .Popen(command,                         stdout=subprocess.PIPE,                         stderr=subprocess.STDOUT)    return iter(p.stdout.readline, b'')

用法与接受的答案相同:

command = 'mysqladmin create test -uroot -pmysqladmin12'.split()for line in run_command(command):    print(line)

#2楼

您的里程可能会有所不同,我尝试使用@senderle在Windows 2.6.5上的Windows中使用Vartec的解决方案,但是我遇到了错误,并且没有其他解决方案起作用。 我的错误是: WindowsError: [Error 6] The handle is invalid

我发现我必须为每个句柄分配PIPE才能使其返回我期望的输出-以下内容对我有用。

import subprocessdef run_command(cmd):    """given shell command, returns communication tuple of stdout and stderr"""    return subprocess.Popen(cmd,                             stdout=subprocess.PIPE,                             stderr=subprocess.PIPE,                             stdin=subprocess.PIPE).communicate()

并像这样调用( [0]获取元组的第一个元素stdout ):

run_command('tracert 11.1.0.1')[0]

学习更多之后,我相信我需要这些管道参数,因为我正在使用不同句柄的自定义系统上工作,因此必须直接控制所有std。

要停止控制台弹出窗口(在Windows中),请执行以下操作:

def run_command(cmd):    """given shell command, returns communication tuple of stdout and stderr"""    # instantiate a startupinfo obj:    startupinfo = subprocess.STARTUPINFO()    # set the use show window flag, might make conditional on being in Windows:    startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW    # pass as the startupinfo keyword argument:    return subprocess.Popen(cmd,                            stdout=subprocess.PIPE,                             stderr=subprocess.PIPE,                             stdin=subprocess.PIPE,                             startupinfo=startupinfo).communicate()run_command('tracert 11.1.0.1')

#3楼

如果您需要在多个文件上运行一个shell命令,那么这对我就成功了。

import osimport subprocess# Define a function for running commands and capturing stdout line by line# (Modified from Vartec's solution because it wasn't printing all lines)def runProcess(exe):        p = subprocess.Popen(exe, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)    return iter(p.stdout.readline, b'')# Get all filenames in working directoryfor filename in os.listdir('./'):    # This command will be run on each file    cmd = 'nm ' + filename    # Run the command and capture the output line by line.    for line in runProcess(cmd.split()):        # Eliminate leading and trailing whitespace        line.strip()        # Split the output         output = line.split()        # Filter the output and print relevant lines        if len(output) > 2:            if ((output[2] == 'set_program_name')):                print filename                print line

编辑:刚刚看到了JF Sebastian的建议的Max Persson的解决方案。 继续前进,并纳入。


#4楼

这是一个棘手超级简单的解决方案,可在许多情况下使用:

import osos.system('sample_cmd > tmp')print open('tmp', 'r').read()

使用命令的输出创建一个临时文件(这里是tmp),您可以从中读取所需的输出。

注释中的额外说明:如果是一次性作业,则可以删除tmp文件。 如果您需要多次执行此操作,则无需删除tmp。

os.remove('tmp')

#5楼

对于以下问题,我对同一问题的口味略有不同:

  1. 当STDOUT消息在STDOUT缓冲区中累积时(即实时)捕获并返回它们。
    • @vartec通过使用生成器和'yield'在上解决了这个问题
      上方关键字
  2. 打印所有STDOUT行( 即使在可以完全读取STDOUT缓冲区之前退出进程
  3. 不要浪费CPU周期以高频率轮询进程
  4. 检查子流程的返回码
  5. 如果获得非零错误返回码,则打印STDERR(与STDOUT分开)。

我结合并调整了先前的答案,以得出以下结论:

import subprocessfrom time import sleepdef run_command(command):    p = subprocess.Popen(command,                         stdout=subprocess.PIPE,                         stderr=subprocess.PIPE,                         shell=True)    # Read stdout from subprocess until the buffer is empty !    for line in iter(p.stdout.readline, b''):        if line: # Don't print blank lines            yield line    # This ensures the process has completed, AND sets the 'returncode' attr    while p.poll() is None:                                                                                                                                                sleep(.1) #Don't waste CPU-cycles    # Empty STDERR buffer    err = p.stderr.read()    if p.returncode != 0:       # The run_command() function is responsible for logging STDERR        print("Error: " + str(err))

此代码将与以前的答案相同地执行:

for line in run_command(cmd):    print(line)

#6楼

我遇到了同样的问题,但是想出了一种非常简单的方法:

import subprocessoutput = subprocess.getoutput("ls -l")print(output)

希望能帮上忙

注意:此解决方案特定于Python3,因为subprocess.getoutput subprocess.getoutput()在Python2中不起作用


#7楼

您可以使用以下命令来运行任何shell命令。 我在ubuntu上使用过它们。

import osos.popen('your command here').read()

注意:自python 2.6起不推荐使用。 现在,您必须使用subprocess.Popen 。 以下是示例

import subprocessp = subprocess.Popen("Your command", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0]print p.split("\n")

#8楼

例如,execute('ls -ahl')区分了三种/四种可能的收益和OS平台:

  1. 无输出,但运行成功
  2. 输出空行,运行成功
  3. 运行失败
  4. 输出一些东西,成功运行

功能如下

def execute(cmd, output=True, DEBUG_MODE=False):"""Executes a bash command.(cmd, output=True)output: whether print shell output to screen, only affects screen display, does not affect returned valuesreturn: ...regardless of output=True/False...        returns shell output as a list with each elment is a line of string (whitespace stripped both sides) from output        could be         [], ie, len()=0 --> no output;            [''] --> output empty line;             None --> error occured, see below        if error ocurs, returns None (ie, is None), print out the error message to screen"""if not DEBUG_MODE:    print "Command: " + cmd    # https://stackoverflow.com/a/40139101/2292993    def _execute_cmd(cmd):        if os.name == 'nt' or platform.system() == 'Windows':            # set stdin, out, err all to PIPE to get results (other than None) after run the Popen() instance            p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)        else:            # Use bash; the default is sh            p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, executable="/bin/bash")        # the Popen() instance starts running once instantiated (??)        # additionally, communicate(), or poll() and wait process to terminate        # communicate() accepts optional input as stdin to the pipe (requires setting stdin=subprocess.PIPE above), return out, err as tuple        # if communicate(), the results are buffered in memory        # Read stdout from subprocess until the buffer is empty !        # if error occurs, the stdout is '', which means the below loop is essentially skipped        # A prefix of 'b' or 'B' is ignored in Python 2;         # it indicates that the literal should become a bytes literal in Python 3         # (e.g. when code is automatically converted with 2to3).        # return iter(p.stdout.readline, b'')        for line in iter(p.stdout.readline, b''):            # # Windows has \r\n, Unix has \n, Old mac has \r            # if line not in ['','\n','\r','\r\n']: # Don't print blank lines                yield line        while p.poll() is None:                                                                                                                                                    sleep(.1) #Don't waste CPU-cycles        # Empty STDERR buffer        err = p.stderr.read()        if p.returncode != 0:            # responsible for logging STDERR             print("Error: " + str(err))            yield None    out = []    for line in _execute_cmd(cmd):        # error did not occur earlier        if line is not None:            # trailing comma to avoid a newline (by print itself) being printed            if output: print line,            out.append(line.strip())        else:            # error occured earlier            out = None    return outelse:    print "Simulation! The command is " + cmd    print ""

#9楼

像这样:

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,

#10楼

这个问题的答案取决于您使用的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或更高版本,并且不需要向后兼容 ,则建议使用 。 它为subprocess模块提供了非常通用的高级API。 要捕获程序的输出,请将subprocess.PIPE标志传递给stdout关键字参数。 然后访问返回的对象的stdout属性:

>>> import subprocess>>> result = subprocess.run(['ls', '-l'], stdout=subprocess.PIPE)>>> result.stdoutb'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函数。 自python 2.7开始提供。

subprocess.check_output(*popenargs, **kwargs)

它采用与Popen相同的参数(请参见下文),并返回一个包含程序输出的字符串。 该答案的开头有一个更详细的用法示例。 在Python 3.5及更高版本中, check_output等效于使用check=Truestdout=PIPE执行run ,并仅返回stdout属性。

您可以传递stderr=subprocess.STDOUT以确保错误消息包含在返回的输出中-但是在某些版本的Python中,将stderr=subprocess.PIPE check_output传递给check_output可能导致 。 如果不考虑安全性,您还可以通过传递shell=True来运行更复杂的Shell命令,如下面的注释所述。

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

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

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

Popen构造函数可以接受不带参数的单个命令 ,也可以接受包含命令的列表作为其第一项,后跟任意数量的参数,每个参数均作为列表中的单独项。 可以帮助将字符串解析为适当格式的列表。 Popen对象还接受用于进程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=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 outfoofoo

注意 ,它表明在某些系统上,您可能需要将stdoutstderrstdin全部设置为PIPE (或DEVNULL )才能使communicate正常工作。

在极少数情况下,您可能需要复杂的实时输出捕获。 的答案提出了一条前进的道路,但如果 ,则communicate以外的方法很容易陷入僵局。

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

笔记

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

通常情况下,每次调用runcheck_output ,或Popen构造函数执行一个程序 。 这意味着没有花哨的bash风格的管道。 如果要运行复杂的Shell命令,则可以传递shell=True ,这三个功能都支持。

但是,这样做会引起 。 如果您除了做轻量级脚本编写外,还不如分别调用每个进程,并将每个进程的输出作为输入通过

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

要么

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

直接连接管道的诱惑力很强; 抵抗它。 否则,您很可能会遇到僵局,或者不得不做类似骇人听闻的事情。

2. Unicode注意事项

check_output在Python 2中返回一个字符串,但在Python 3中返回一个bytes对象。如果您还没有时间那么值得花一点时间。


#11楼

subprocess拆分初始命令可能很棘手且麻烦。

使用shlex.split()帮助自己。

样例命令

git log -n 5 --since "5 years ago" --until "2 year ago"

编码

from subprocess import check_outputfrom shlex import splitres = check_output(split('git log -n 5 --since "5 years ago" --until "2 year ago"'))print(res)>>> b'commit 7696ab087a163e084d6870bb4e5e4d4198bdc61a\nAuthor: Artur Barseghyan...'

没有shlex.split() ,代码将如下所示

res = check_output([    'git',     'log',     '-n',     '5',     '--since',     '5 years ago',     '--until',     '2 year ago'])print(res)>>> b'commit 7696ab087a163e084d6870bb4e5e4d4198bdc61a\nAuthor: Artur Barseghyan...'

#12楼

根据@senderle,如果您像我一样使用python3.6:

def sh(cmd, input=""):    rst = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, input=input.encode("utf-8"))    assert rst.returncode == 0, rst.stderr.decode("utf-8")    return rst.stdout.decode("utf-8")
sh("ls -a")

就像您在bash中运行命令一样


#13楼

如果使用subprocess python模块,则可以分别处理STDOUT,STDERR和命令的返回代码。 您可以看到完整的命令调用程序实现的示例。 当然,您可以使用try..except扩展它, try..except需要。

下面的函数返回STDOUT,STDERR和Return代码,因此您可以在其他脚本中处理它们。

import subprocessdef command_caller(command=None)    sp = subprocess.Popen(command, stderr=subprocess.PIPE, stdout=subprocess.PIPE, shell=False)    out, err = sp.communicate()    if sp.returncode:        print(            "Return code: %(ret_code)s Error message: %(err_msg)s"            % {"ret_code": sp.returncode, "err_msg": err}            )    return sp.returncode, out, err

#14楼

可以将输出重定向到文本文件,然后将其读回。

import subprocessimport osimport tempfiledef execute_to_file(command):    """    This function execute the command    and pass its output to a tempfile then read it back    It is usefull for process that deploy child process    """    temp_file = tempfile.NamedTemporaryFile(delete=False)    temp_file.close()    path = temp_file.name    command = command + " > " + path    proc = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)    if proc.stderr:        # if command failed return        os.unlink(path)        return    with open(path, 'r') as f:        data = f.read()    os.unlink(path)    return dataif __name__ == "__main__":    path = "Somepath"    command = 'ecls.exe /files ' + path    print(execute(command))

#15楼

这很容易,但仅适用于Unix(包括Cygwin)和Python2.7。

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

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

对于在这两个Python2和Python3有效的解决方案,使用subprocess模块来代替:

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

转载地址:http://ovdnb.baihongyu.com/

你可能感兴趣的文章
python列表按照多个条件排序
查看>>
Django2.x path使用方法
查看>>
Django为模型初始化数据
查看>>
Django工程目录结构的最佳实践
查看>>
数据库多级联动表结构设计
查看>>
软件开发六大设计原则
查看>>
使用Maven项目搭建SpringBoot框架的简单web项目。
查看>>
Cannot change version of project facet Dynamic Web Module to 3.0
查看>>
Maven的聚合与继承DEMO(以及前后端分离)
查看>>
线程工厂DEMO(静态工厂,实例工厂)
查看>>
设计模式:工厂方法模式
查看>>
SQL进阶:HAVING子句
查看>>
eclipse中安装lombok
查看>>
Oracle客户端连接工具的链接配置
查看>>
eclipse导入基本注释模板
查看>>
注解保留策略说明
查看>>
并发编程:执行器:1、添加拒绝策略(RejectedExecutionHandler )
查看>>
并发编程:执行器:2、执行N个任务,并返回结果(Callable和Future)
查看>>
并发编程:执行器:3、运行N个任务并处理第一个返回结果(invokeAny() )
查看>>
No qualifying bean of type 'com.funo.oa.dao.PostDao' available问题解决:找不到dao的实现类
查看>>