Python 运行 shell 命令并捕获输出

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4760215/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 17:23:03  来源:igfitidea点击:

Running shell command and capturing the output

pythonshellsubprocess

提问by Silver Light

I want to write a function that will execute a shell command and return its output as a string, no matter, is it an error or success message. I just want to get the same result that I would have gotten with the command line.

我想编写一个函数,该函数将执行 shell 命令并将其输出作为 string返回,无论它是错误还是成功消息。我只想获得与使用命令行获得的结果相同的结果。

What would be a code example that would do such a thing?

会做这样的事情的代码示例是什么?

For example:

例如:

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'

采纳答案by senderle

The answer to this question depends on the version of Python you're using. The simplest approach is to use the subprocess.check_outputfunction:

这个问题的答案取决于您使用的 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_outputruns a single program that takes only arguments as input.1It returns the result exactly as printed to stdout. If you need to write input to stdin, skip ahead to the runor Popensections. If you want to execute complex shell commands, see the note on shell=Trueat the end of this answer.

check_output运行一个只接受参数作为输入的程序。1它返回与打印完全一样的结果stdout。如果您需要将输入写入stdin,请跳至runPopen部分。如果要执行复杂的 shell 命令,请参阅shell=True本答案末尾的注释。

The check_outputfunction works on almost all versions of Python still in wide use (2.7+).2But for more recent versions, it is no longer the recommended approach.

check_output函数适用于几乎所有仍在广泛使用的 Python 版本(2.7+)。2但对于较新的版本,它不再是推荐的方法。

Modern versions of Python (3.5 or higher): run

Python 的现代版本(3.5 或更高版本): run

If you're using Python 3.5or higher, and do not need backwards compatibility, the new runfunctionis recommended. It provides a very general, high-level API for the subprocessmodule. To capture the output of a program, pass the subprocess.PIPEflag to the stdoutkeyword argument. Then access the stdoutattribute of the returned CompletedProcessobject:

如果您使用的是Python 3.5或更高版本,并且不需要向后兼容,则建议使用run函数。它为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'

The return value is a bytesobject, so if you want a proper string, you'll need to decodeit. Assuming the called process returns a UTF-8-encoded string:

返回值是一个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'

This can all be compressed to a one-liner:

这都可以压缩为一个单行:

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

If you want to pass input to the process's stdin, pass a bytesobject to the inputkeyword argument:

如果要将输入传递给进程的stdinbytes请将对象传递给input关键字参数:

>>> cmd = ['awk', 'length(
subprocess.check_output(*popenargs, **kwargs)  
) > 5'] >>> input = 'foo\nfoofoo\n'.encode('utf-8') >>> result = subprocess.run(cmd, stdout=subprocess.PIPE, input=input) >>> result.stdout.decode('utf-8') 'foofoo\n'

You can capture errors by passing stderr=subprocess.PIPE(capture to result.stderr) or stderr=subprocess.STDOUT(capture to result.stdoutalong with regular output). When security is not a concern, you can also run more complex shell commands by passing shell=Trueas described in the notes below.

您可以通过传递stderr=subprocess.PIPE(capture to result.stderr) 或stderr=subprocess.STDOUT(capture toresult.stdout以及常规输出)来捕获错误。当安全性不是问题时,您还可以通过传递shell=True如下注释中所述来运行更复杂的 shell 命令。

This adds just a bit of complexity, compared to the old way of doing things. But I think it's worth the payoff: now you can do almost anything you need to do with the runfunction alone.

与旧的做事方式相比,这只是增加了一点复杂性。但我认为这是值得的:现在你几乎可以用这个run函数做任何你需要做的事情。

Older versions of Python (2.7-3.4): check_output

旧版本的 Python (2.7-3.4): check_output

If you are using an older version of Python, or need modest backwards compatibility, you can probably use the check_outputfunction as briefly described above. It has been available since Python 2.7.

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

output = subprocess.Popen(["mycmd", "myarg"], 
                          stdout=subprocess.PIPE).communicate()[0]

It takes takes the same arguments as Popen(see below), and returns a string containing the program's output. The beginning of this answer has a more detailed usage example. In Python 3.5 and greater, check_outputis equivalent to executing runwith check=Trueand stdout=PIPE, and returning just the stdoutattribute.

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

You can pass stderr=subprocess.STDOUTto ensure that error messages are included in the returned output -- but in some versions of Python passing stderr=subprocess.PIPEto check_outputcan cause deadlocks. When security is not a concern, you can also run more complex shell commands by passing shell=Trueas described in the notes below.

您可以传递stderr=subprocess.STDOUT以确保错误消息包含在返回的输出中——但在某些版本的 Python 中传递stderr=subprocess.PIPEcheck_output可能会导致死锁。当安全性不是问题时,您还可以通过传递shell=True如下注释中所述来运行更复杂的 shell 命令。

If you need to pipe from stderror pass input to the process, check_outputwon't be up to the task. See the Popenexamples below in that case.

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

Complex applications & legacy versions of Python (2.6 and below): Popen

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

If you need deep backwards compatibility, or if you need more sophisticated functionality than check_outputprovides, you'll have to work directly with Popenobjects, which encapsulate the low-level API for subprocesses.

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

The Popenconstructor accepts either a single commandwithout arguments, or a listcontaining a command as its first item, followed by any number of arguments, each as a separate item in the list. shlex.splitcan help parse strings into appropriately formatted lists. Popenobjects also accept a host of different argumentsfor process IO management and low-level configuration.

所述Popen构造器接受单个命令没有参数,或列表包含指令作为其第一项,其次是任意数量的参数,每个作为列表一个单独的项目。shlex.split可以帮助将字符串解析为适当格式的列表。Popen对象还接受大量不同的参数用于进程 IO 管理和低级配置。

To send input and capture output, communicateis almost always the preferred method. As in:

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

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

Or

或者

>>> cmd = ['awk', 'length(
run(cmd, [stdout=etc...], input=other_output)
) > 5'] >>> p = subprocess.Popen(cmd, stdout=subprocess.PIPE, ... stderr=subprocess.PIPE, ... stdin=subprocess.PIPE) >>> out, err = p.communicate('foo\nfoofoo\n') >>> print out foofoo

If you set stdin=PIPE, communicatealso allows you to pass data to the process via stdin:

如果设置stdin=PIPEcommunicate还允许您通过以下方式将数据传递给进程stdin

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

Note Aaron Hall's answer, which indicates that on some systems, you may need to set stdout, stderr, and stdinall to PIPE(or DEVNULL) to get communicateto work at all.

注意艾伦·霍尔的回答,这表明在某些系统上,你可能需要设置stdoutstderr以及stdin所有PIPE(或DEVNULL)得到communicate工作的。

In some rare cases, you may need complex, real-time output capturing. Vartec's answer suggests a way forward, but methods other than communicateare prone to deadlocks if not used carefully.

在极少数情况下,您可能需要复杂的实时输出捕获。Vatec的回答提出了一条前进的道路,但communicate如果不小心使用,除此之外的方法很容易出现死锁。

As with all the above functions, when security is not a concern, you can run more complex shell commands by passing shell=True.

与上述所有函数一样,当安全性不是问题时,您可以通过传递shell=True.

Notes

笔记

1. Running shell commands: the shell=Trueargument

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

Normally, each call to run, check_output, or the Popenconstructor executes a single program. That means no fancy bash-style pipes. If you want to run complex shell commands, you can pass shell=True, which all three functions support.

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

However, doing so raises security concerns. If you're doing anything more than light scripting, you might be better off calling each process separately, and passing the output from each as an input to the next, via

但是,这样做会引起安全问题。如果你做的不仅仅是轻量级脚本,你最好分别调用每个进程,并将每个进程的输出作为输入传递给下一个进程,通过

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

Or

或者

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

The temptation to directly connect pipes is strong; resist it. Otherwise, you'll likely see deadlocks or have to do hacky things like this.

直接连接管道的诱惑力很强;抵制它。否则,你可能会看到死锁或有做这样的事情哈克这样

2. Unicode considerations

2. Unicode 考虑

check_outputreturns a string in Python 2, but a bytesobject in Python 3. It's worth taking a moment to learn about unicodeif you haven't already.

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

回答by vartec

Something like that:

类似的东西:

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

Note, that I'm redirecting stderr to stdout, it might not be exactly what you want, but I want error messages also.

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

This function yields line by line as they come(normally you'd have to wait for subprocess to finish to get the output as a whole).

这个函数在它们来的时候一行一行地产生(通常你必须等待子进程完成才能获得整个输出)。

For your case the usage would be:

对于您的情况,用法是:

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

回答by byte_array

This is way easier, but only works on Unix (including Cygwin) and Python2.7.

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

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

It returns a tuple with the (return_value, output).

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

For a solution that works in both Python2 and Python3, use the subprocessmodule instead:

对于适用于 Python2 和 Python3 的解决方案,请改用该subprocess模块:

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

回答by Max Ekman

Vartec'sanswer doesn't read all lines, so I made a version that did:

Vatec 的回答没有读取所有行,所以我做了一个版本:

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

Usage is the same as the accepted answer:

用法与接受的答案相同:

run_command('tracert 11.1.0.1')[0]

回答by Aaron Hall

Your Mileage May Vary, I attempted @senderle's spin on Vartec's solution in Windows on Python 2.6.5, but I was getting errors, and no other solutions worked. My error was: WindowsError: [Error 6] The handle is invalid.

您的里程可能会有所不同,我尝试在 Python 2.6.5 上的 Windows 中使用 @senderle 对 Vartec 的解决方案进行旋转,但出现错误,并且没有其他解决方案有效。我的错误是:WindowsError: [Error 6] The handle is invalid

I found that I had to assign PIPE to every handle to get it to return the output I expected - the following worked for me.

我发现我必须将 PIPE 分配给每个句柄才能让它返回我期望的输出 - 以下对我有用。

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

and call like this, ([0]gets the first element of the tuple, stdout):

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

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

After learning more, I believe I need these pipe arguments because I'm working on a custom system that uses different handles, so I had to directly control all the std's.

了解更多之后,我相信我需要这些管道参数,因为我正在使用不同句柄的自定义系统,所以我不得不直接控制所有标准。

To stop console popups (with Windows), do this:

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

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

回答by Ethan Strider

If you need to run a shell command on multiple files, this did the trick for me.

如果您需要在多个文件上运行 shell 命令,这对我有用。

os.remove('tmp')

Edit: Just saw Max Persson's solution with J.F. Sebastian's suggestion. Went ahead and incorporated that.

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

回答by Mehdi Saman Booy

This is a trickybut super simplesolution which works in many situations:

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

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

A temporary file(here is tmp) is created with the output of the command and you can read from it your desired output.

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

Extra note from the comments: You can remove the tmp file in the case of one-time job. If you need to do this several times, there is no need to delete the tmp.

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

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

回答by The Aelfinn

I had a slightly different flavor of the same problem with the following requirements:

对于相同的问题,我对以下要求略有不同:

  1. Capture and return STDOUT messages as they accumulate in the STDOUT buffer (i.e. in realtime).
    • @vartec solved this Pythonically with his use of generators and the 'yield'
      keyword above
  2. Print all STDOUT lines (even if process exits before STDOUT buffer can be fully read)
  3. Don't waste CPU cycles polling the process at high-frequency
  4. Check the return code of the subprocess
  5. Print STDERR (separate from STDOUT) if we get a non-zero error return code.
  1. 当 STDOUT 消息在 STDOUT 缓冲区中累积时(即实时)捕获并返回 STDOUT 消息。
    • @vartec 通过使用生成器和
      上面的 'yield'关键字以 Python 方式解决了这个问题
  2. 打印所有 STDOUT 行(即使进程在可以完全读取 STDOUT 缓冲区之前退出
  3. 不要浪费 CPU 周期以高频轮询进程
  4. 检查子进程的返回码
  5. 如果我们得到非零错误返回码,则打印 STDERR(与 STDOUT 分开)。

I've combined and tweaked previous answers to come up with the following:

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

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

This code would be executed the same as previous answers:

此代码的执行方式与之前的答案相同:

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

回答by azhar22k

I had the same problem but figured out a very simple way of doing this:

我有同样的问题,但想出了一个非常简单的方法来做到这一点:

import subprocess

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

Hope it helps out

希望它有所帮助

Note: This solution is Python3 specific as subprocess.getoutput()doesn't work in Python2

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

回答by Muhammad Hassan

You can use following commands to run any shell command. I have used them on ubuntu.

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

##代码##

Note:This is deprecated since python 2.6. Now you must use subprocess.Popen. Below is the example

注意:自 python 2.6 起已弃用。现在您必须使用subprocess.Popen. 下面是例子

##代码##