从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
,请跳至run
或Popen
部分。 如果要执行复杂的shell命令,请参阅本答案末尾处的shell=True
注释。
check_output
函数几乎可以在广泛使用的所有Python版本上运行(2.7+)。 2但是对于更新的版本,它不再是推荐的方法。
现代版本的Python(3.5或更高版本): run
如果您使用Python 3.5或更高版本,并且不需要向后兼容性 ,则build议使用新的run
function 。 它为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命令,如下面的注释中所述。
与旧的做事方式相比,这增加了一点复杂性。 但是我认为这是值得的:现在你几乎可以做任何你需要做的run
function。
较早的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命令,如下面的注释中所述。
如果您需要从stderr
pipe道或将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=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 out foofoo
注意Aaron Hall的回答 ,这表明在某些系统上,可能需要将stdout
, stderr
和stdin
都设置为PIPE
(或DEVNULL
)才能使communicate
正常工作。
在极less数情况下,您可能需要复杂的实时输出捕获。 Vartec的答案提出了一条前进的道路,但如果不谨慎使用 , communicate
以外的方法容易出现僵局。
与上述所有function一样,当安全性不是问题时,您可以通过传递shell=True
来运行更复杂的shell命令。
笔记
1.运行shell命令: shell=True
参数
通常,每次调用run
, check_output
或check_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)
我有一个稍微不同的风味,同样的问题与以下要求:
- 在STDOUT缓冲区中累积(即实时)时捕获并返回STDOUT消息。
- @vartec通过使用发生器和“yield”来解决这个问题,
以上关键字
- @vartec通过使用发生器和“yield”来解决这个问题,
- 打印所有STDOUT行( 即使进程退出之前STDOUT缓冲区可以完全读取 )
- 不要浪费CPU周期以高频率轮询过程
- 检查subprocess的返回码
- 打印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')区分的三/四种可能的回报和操作系统平台:
- 没有输出,但运行成功
- 输出空行,运行成功
- 运行失败
- 输出一些东西,运行成功
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 ""