运行shell命令并捕获输出

时间:2011-01-21 14:55:44

标签: python shell subprocess

我想写一个执行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'

21 个答案:

答案 0 :(得分:896)

答案 1 :(得分:178)

这样更容易,但只适用于Unix(包括Cygwin)。

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

它返回一个带有(return_value,output)

的元组

此功能仅适用于python2.7python3无效。对于兼容两者的解决方案,请改用subprocess模块:

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

答案 2 :(得分:104)

类似的东西:

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,

答案 3 :(得分:60)

Vartec's回答没有读取所有行,所以我做了一个版本:

def run_command(command):
    p = subprocess.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)

答案 4 :(得分:41)

这是一个棘手的超级简单解决方案,适用于多种情况:

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

使用命令的输出创建临时文件(此处为tmp),您可以从中读取所需的输出。

评论中的额外说明: 您可以在一次性作业的情况下删除tmp文件。如果您需要多次执行此操作,则无需删除tmp。

os.remove('tmp')

答案 5 :(得分:22)

在Python 3.5中:

import subprocess

output = subprocess.run("ls -l", shell=True, stdout=subprocess.PIPE, 
                        universal_newlines=True)
print(output.stdout)

答案 6 :(得分:18)

我遇到了同样的问题 但想出了一个非常简单的方法 按照这个

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

希望它有所帮助

注意:此解决方案是python3特定的,因为subprocess.getoutput()无法在python2中工作

答案 7 :(得分:15)

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

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

答案 8 :(得分:13)

现代Python解决方案(> = 3.1):

 res = subprocess.check_output(lcmd, stderr=subprocess.STDOUT)

答案 9 :(得分:10)

你的里程可能会变化,我试图在@ 2.6中使用@remitle在Windows上的Vartec解决方案,但是我遇到了错误,没有其他解决方案有效。我的错误是:WindowsError: [Error 6] The handle is invalid

我发现我必须将PIPE分配给每个句柄以使其返回我预期的输出 - 以下内容对我有效。

import subprocess

def 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')

答案 10 :(得分:8)

我对同样问题的描述略有不同,符合以下要求:

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

    import subprocess
    from time import sleep
    
    def 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)
    

答案 11 :(得分:2)

如果你需要对多个文件运行一个shell命令,这对我来说就是一个诀窍。

import os
import 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 directory
for 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

编辑:刚刚看到马克斯佩尔森的解决方案与J.F.塞巴斯蒂安的建议。走在前面并将其纳入其中。

答案 12 :(得分:2)

subprocess分割初始命令可能很棘手且繁琐。

使用shlex.split来帮助自己。

示例命令

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

代码

from subprocess import check_output
from shlex import split

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

答案 13 :(得分:1)

这是一个解决方案,可以在进程是否运行时打印输出。


我还添加了当前的工作目录,这对我不止一次有用。


希望解决方案对某人有所帮助:)。

import subprocess

def run_command(cmd_and_args, print_constantly=False, cwd=None):
"""Runs a system command.

:param cmd_and_args: the command to run with or without a Pipe (|).
:param print_constantly: If True then the output is logged in continuous until the command ended.
:param cwd: the current working directory (the directory from which you will like to execute the command)
:return: - a tuple containing the return code, the stdout and the stderr of the command
"""
output = []

process = subprocess.Popen(cmd_and_args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd)

while True:
    next_line = process.stdout.readline()
    if next_line:
        output.append(str(next_line))
        if print_constantly:
            print(next_line)
    elif not process.poll():
        break

error = process.communicate()[1]

return process.returncode, '\n'.join(output), error

答案 14 :(得分:1)

Python 3.7+ 上,使用 subprocess.run 并传递 capture_output=True

import subprocess
result = subprocess.run(['echo', 'hello', 'world'], capture_output=True)
print(repr(result.stdout))

这将返回字节:

b'hello world\n'

如果你想让它把字节转换成字符串,添加text=True

result = subprocess.run(['echo', 'hello', 'world'], capture_output=True, text=True)
print(repr(result.stdout))

这将使用您的默认编码读取字节:

'hello world\n'

如果您需要手动指定不同的编码,请使用 encoding="your encoding" 而不是 text=True

result = subprocess.run(['echo', 'hello', 'world'], capture_output=True, encoding="utf8")
print(repr(result.stdout))

答案 15 :(得分:0)

例如,执行(' ls -ahl') 差异化的三/四种可能的回报和操作系统平台:

  1. 无输出,但成功运行
  2. 输出空行,成功运行
  3. 运行失败
  4. 输出内容,成功运行
  5. 以下功能

    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 values
    return: ...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 out
    else:
        print "Simulation! The command is " + cmd
        print ""
    

答案 16 :(得分:0)

根据@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中运行命令一样

答案 17 :(得分:0)

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

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

import subprocess

def 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

答案 18 :(得分:0)

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

import subprocess
import os
import tempfile

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

if __name__ == "__main__":
    path = "Somepath"
    command = 'ecls.exe /files ' + path
    print(execute(command))

答案 19 :(得分:0)

只写了一个小bash脚本即可使用curl

https://gist.github.com/harish2704/bfb8abece94893c53ce344548ead8ba5

#!/usr/bin/env bash

# Usage: gdrive_dl.sh <url>

urlBase='https://drive.google.com'
fCookie=tmpcookies

curl="curl -L -b $fCookie -c $fCookie"
confirm(){
    $curl "$1" | grep jfk-button-action | sed -e 's/.*jfk-button-action" href="\(\S*\)".*/\1/' -e 's/\&amp;/\&/g'
}

$curl -O -J "${urlBase}$(confirm $1)"

答案 20 :(得分:0)

我建议您选择simppl作为考虑的选项。该模块可通过pypi:pip install simppl使用,并在python3上运行。

simppl允许用户运行shell命令并从屏幕读取输出。

开发人员建议了三种类型的用例:

  1. 最简单的用法如下:
    from simppl.simple_pipeline import SimplePipeline
    sp = SimplePipeline(start=0, end=100):
    sp.print_and_run('<YOUR_FIRST_OS_COMMAND>')
    sp.print_and_run('<YOUR_SECOND_OS_COMMAND>') ```

  1. 要同时运行多个命令,请使用:
    commands = ['<YOUR_FIRST_OS_COMMAND>', '<YOUR_SECOND_OS_COMMAND>']
    max_number_of_processes = 4
    sp.run_parallel(commands, max_number_of_processes) ```

  1. 最后,如果您的项目使用cli模块,则可以作为管道的一部分直接运行另一个command_line_tool。另一个工具将 可以从相同的进程运行,但是它将在日志中显示为 管道中的另一个命令。这样可以使调试更加顺畅,并且 重构工具调用其他工具。
    from example_module import example_tool
    sp.print_and_run_clt(example_tool.run, ['first_number', 'second_nmber'], 
                                 {'-key1': 'val1', '-key2': 'val2'},
                                 {'--flag'}) ```

请注意,通过python的logging模块可以打印到STDOUT / STDERR。


下面是完整的代码,用于显示simppl的工作方式:

import logging
from logging.config import dictConfig

logging_config = dict(
    version = 1,
    formatters = {
        'f': {'format':
              '%(asctime)s %(name)-12s %(levelname)-8s %(message)s'}
        },
    handlers = {
        'h': {'class': 'logging.StreamHandler',
              'formatter': 'f',
              'level': logging.DEBUG}
        },
    root = {
        'handlers': ['h'],
        'level': logging.DEBUG,
        },
)
dictConfig(logging_config)

from simppl.simple_pipeline import SimplePipeline
sp = SimplePipeline(0, 100)
sp.print_and_run('ls')