从Python运行shell命令并捕获输出

我想编写一个函数来执行一个shell命令并将其输出作为一个string返回,不pipe它是一个错误还是成功的消息。 我只想得到和命令行一样的结果。

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

例如:

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运行一个只有参数作为input的程序。 1它返回的结果与打印到stdout完全一样。 如果您需要将input写入stdin ,请跳至runPopen部分。 如果要执行复杂的shell命令,请参阅本答案末尾处的shell=True注释。

check_output函数几乎可以在广泛使用的所有Python版本上运行(2.7+)。 2但是对于更新的版本,它不再是推荐的方法。

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

如果您使用Python 3.5或更高版本,并且不需要向后兼容性 ,则build议使用新的runfunction 。 它为subprocess模块提供了一个非常通用的高级API。 要捕获程序的输出,请将subprocess.PIPE标志传递给stdout关键字参数。 然后访问返回的CompletedProcess对象的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对象,所以如果你想要一个正确的string,你需要decode它。 假设被调用的进程返回一个UTF-8编码的string:

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

如果要将input传递给进程的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命令,如下面的注释中所述。

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

较早的Python版本(2.7-3.4): check_output

如果您使用的是旧版本的Python,或者需要适度的向后兼容性,则可以使用check_output函数,如上所述。 自Python 2.7以来已经可用。

 subprocess.check_output(*popenargs, **kwargs) 

它采用与Popen相同的参数(见下文),并返回一个包含程序输出的string。 这个答案的开头有一个更详细的使用例子。

您可以传递stderr=subprocess.STDOUT以确保返回的输出中包含错误消息,但不要将stderr=subprocess.PIPE check_output传递给check_output 。 它可能导致死锁 。 当不担心安全问题时,还可以通过传递shell=True来运行更复杂的shell命令,如下面的注释中所述。

如果您需要从stderrpipe道或将input传递给进程, check_output将不会执行任务。 在这种情况下请看下面的Popen例子。

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

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

Popen构造函数接受一个不带参数的单个命令 ,或一个包含一个命令作为其第一个项目的列表 ,后面跟随任意数量的参数,每个参数都作为列表中的单独项目。 shlex.split可以帮助将stringparsing为适当格式的列表。 Popen对象还接受许多用于进程IOpipe理和低级configuration的不同参数 。

要发送input和捕获输出, 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的回答 ,这表明在某些系统上,可能需要将stdoutstderrstdin都设置为PIPE (或DEVNULL )才能使communicate正常工作。

在极less数情况下,您可能需要复杂的实时输出捕获。 Vartec的答案提出了一条前进的道路,但如果不谨慎使用 , communicate以外的方法容易出现僵局。

与上述所有function一样,当安全性不是问题时,您可以通过传递shell=True来运行更复杂的shell命令。

笔记

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

通常,每次调用runcheck_outputcheck_output构造函数都会执行一个程序 。 这意味着没有花哨的bash式pipe道。 如果你想运行复杂的shell命令,你可以通过shell=True ,这三个函数都支持。

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

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

要么

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

直接连接pipe道的诱惑力很强。 抵制它。 否则,你可能会看到僵局,或者不得不做这样的黑客事情。

2. Unicode考虑

check_output在Python 2中返回一个string,但是在Python 3中返回一个bytes对象。如果你还没有学习unicode,那么值得check_output 学习一下 。

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

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

它返回一个元组(return_value,output)

类似的东西:

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

请注意,我将stderrredirect到stdout,它可能不是您想要的,但我也想要错误消息。

这个函数一行一行地产生 (通常你必须等待subprocess完成才能得到输出)。

对于你的情况使用将是:

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

Vartec的回答并不是所有的行都是这样,所以我做了一个版本:

 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) 

这是一个棘手的,超级简单的解决scheme,在许多情况下工作:

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

一个临时文件(这里是tmp)是用命令的输出创build的,你可以从中读取你想要的输出。

备注:注意:一次性作业可以删除tmp文件。 如果您需要多次执行此操作,则不需要删除该tmp。

 os.remove('tmp') 

现代Python解决scheme(> = 3.1):

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

您的里程可能会有所不同,我试着在Windows 2.6上使用Vartec的解决scheme,但是我遇到了错误,没有其他的解决scheme。 我的错误是: 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] 

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

要停止控制台popup窗口(使用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') 

在Python 3.5中:

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

我有一个稍微不同的风味,同样的问题与以下要求:

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

我已经结合并调整了以前的答案,以提出以下几点:

 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) 

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

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

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

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

希望它有帮助

注意:这个解决scheme是python3,因为subprocess.getoutput()在python2中不起作用

如果你需要在多个文件上运行一个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 

编辑:刚刚看到最大佩尔松的解决scheme与JF塞巴斯蒂安的build议。 提前并纳入。

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

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

function如下

 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 # (eg 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 ""