我想编写一个函数,该函数将执行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'
对于相同的问题,我有一个稍微不同的需求:
捕获并返回STDOUT消息,因为它们在STDOUT缓冲区中累积(即实时)。
@vartec用python方法解决了这个问题,他使用生成器和“yield”
上面的字
打印所有STDOUT行(即使进程在STDOUT缓冲区可以完全读取之前退出)
不要浪费CPU周期以高频轮询进程
检查子流程的返回代码
如果得到非零错误返回码,则打印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)
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)
拆分子进程的初始命令可能非常棘手和麻烦。
使用shlex.split()来帮助自己。
示例命令
Git log -n 5——5年前到2年前
的代码
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...'
如果没有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...'
就像这样:
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,