Running shell command and capturing the output
我想编写一个函数,该函数将执行一个shell命令并将其输出作为字符串返回,不管它是错误消息还是成功消息。我只想得到和命令行相同的结果。
什么样的代码示例可以做到这一点?
例如:
1 2 3 4 5 6 | 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版本。最简单的方法是使用
1 2 3 4 | >>> subprocess.check_output(['ls', '-l']) b'total 0 -rw-r--r-- 1 memyself staff 0 Mar 14 11:04 files ' |
如果您使用的是python 3.5或更高版本,并且不需要向后兼容,建议使用新的
1 2 3 4 5 6 | >>> import subprocess >>> result = subprocess.run(['ls', '-l'], stdout=subprocess.PIPE) >>> result.stdout b'total 0 -rw-r--r-- 1 memyself staff 0 Mar 14 11:04 files ' |
返回值是一个
1 2 3 4 | >>> result.stdout.decode('utf-8') 'total 0 -rw-r--r-- 1 memyself staff 0 Mar 14 11:04 files ' |
所有这些都可以压缩为一个内衬:好的。
1 2 3 4 | >>> subprocess.run(['ls', '-l'], stdout=subprocess.PIPE).stdout.decode('utf-8') 'total 0 -rw-r--r-- 1 memyself staff 0 Mar 14 11:04 files ' |
如果要将输入传递给进程的
1 2 3 4 5 6 7 8 | >>> cmd = ['awk', 'length($0) > 5'] >>> input = 'foo foofoo '.encode('utf-8') >>> result = subprocess.run(cmd, stdout=subprocess.PIPE, input=input) >>> result.stdout.decode('utf-8') 'foofoo ' |
您可以通过传递
与以前的做事方式相比,这只增加了一点复杂性。但我认为这是值得的回报:现在你可以做几乎任何你需要做的事情与
如果您使用的是旧版本的python,或者需要适度的向后兼容性,那么您可能可以像上面简要描述的那样使用
1 | subprocess.check_output(*popenargs, **kwargs) |
它采用与
可以传递
如果需要从
如果您需要深层次的向后兼容性,或者如果您需要比
为了发送输入和捕获输出,
1 2 | output = subprocess.Popen(["mycmd","myarg"], stdout=subprocess.PIPE).communicate()[0] |
或好的。
1 2 3 4 5 6 7 8 | >>> import subprocess >>> p = subprocess.Popen(['ls', '-a'], stdout=subprocess.PIPE, ... stderr=subprocess.PIPE) >>> out, err = p.communicate() >>> print out . .. foo |
如果设置
1 2 3 4 5 6 7 8 9 | >>> cmd = ['awk', 'length($0) > 5'] >>> p = subprocess.Popen(cmd, stdout=subprocess.PIPE, ... stderr=subprocess.PIPE, ... stdin=subprocess.PIPE) >>> out, err = p.communicate('foo foofoo ') >>> print out foofoo |
注意Aaron Hall的回答,这表明在某些系统上,您可能需要将
在一些罕见的情况下,您可能需要复杂的实时输出捕获。Vartec的回答表明了一条前进的道路,但是除
和上面所有的函数一样,当安全性不是问题时,您可以通过传递
1。运行shell命令:
通常,每个对
但是,这样做会引起安全问题。如果您做的不仅仅是简单的脚本编写,那么最好分别调用每个进程,并将每个进程的输出作为输入传递给下一个进程,通过好的。
1 | run(cmd, [stdout=etc...], input=other_output) |
或好的。
1 | Popen(cmd, [stdout=etc...]).communicate(other_output) |
直接连接管道的诱惑很强;抵制它。否则,您可能会看到死锁,或者不得不做类似的事情。好的。
2。Unicode注意事项好的。
这很容易,但只在Unix(包括Cygwin)上工作。
1 2 | import commands print commands.getstatusoutput('wc -l file') |
它返回一个具有(返回值,输出)的元组
这只在
1 2 3 4 | import subprocess output=subprocess.Popen(["date"],stdout=PIPE) response=output.communicate() print response |
就像这样:
1 2 3 4 5 6 7 8 9 | 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,这可能不是您想要的,但我也需要错误消息。
这个函数在一行一行地生成它们(通常您需要等待子进程完成才能获得整个输出)。
对于您的案例,用法是:
1 2 | for line in runProcess('mysqladmin create test -uroot -pmysqladmin12'.split()): print line, |
Vartec的答案并不是所有的行,所以我做了一个版本:
1 2 3 4 5 | def run_command(command): p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) return iter(p.stdout.readline, b'') |
用法与接受的答案相同:
1 2 3 | command = 'mysqladmin create test -uroot -pmysqladmin12'.split() for line in run_command(command): print(line) |
这是一个非常复杂但非常简单的解决方案,适用于许多情况:
1 2 3 | import os os.system('sample_cmd > tmp') print open('tmp', 'r').read() |
使用命令的输出创建一个临时文件(这里是tmp),您可以从中读取所需的输出。
评论中的额外说明:对于一次性作业,可以删除tmp文件。如果需要多次执行此操作,则无需删除tmp。
1 | os.remove('tmp') |
在Python 3.5中:
1 2 3 4 5 | import subprocess output = subprocess.run("ls -l", shell=True, stdout=subprocess.PIPE, universal_newlines=True) print(output.stdout) |
我也有同样的问题但找到了一个非常简单的方法遵循这一点
1 2 3 | import subprocess output = subprocess.getoutput("ls -l") print(output) |
希望能有所帮助
注:该溶液为python3专用溶液,因为
可以使用以下命令运行任何shell命令。我在Ubuntu上用过。
1 2 | import os os.popen('your command here').read() |
现代python解决方案(>=3.1):
1 | res = subprocess.check_output(lcmd, stderr=subprocess.STDOUT) |
您的里程数可能会有所不同,我在python 2.6.5上尝试了@senderle对vartec在windows中的解决方案进行旋转,但我遇到了错误,没有其他解决方案起作用。我的错误是:
我发现我必须为每个句柄分配pipe,以使其返回我期望的输出——以下内容对我很有用。
1 2 3 4 5 6 7 8 | 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() |
这样调用,(
1 | run_command('tracert 11.1.0.1')[0] |
在了解了更多之后,我相信我需要这些管道参数,因为我正在使用不同的句柄的定制系统上工作,所以我必须直接控制所有的STD。
要停止控制台弹出窗口(使用Windows),请执行以下操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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') |
我对同样的问题有一个稍微不同的口味,有以下要求:
- @瓦特克用发电机和"产量"解决了这个难题。关键字以上
我结合并调整了以前的答案,得出以下结论:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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)) |
此代码的执行方式与之前的答案相同:
1 2 | for line in run_command(cmd): print(line) |
如果你需要在多个文件上运行shell命令,这对我来说就是一个技巧。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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.塞巴斯蒂安的建议。继续进行合并。
分割
用
样本命令
代码
1 2 3 4 5 6 7 | 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 Author: Artur Barseghyan...' |
例如,execute("ls-ahl")。区分三/四种可能的回报和操作系统平台:
函数如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | 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 , Unix has , Old mac has # if line not in ['',' ',' ',' ']: # 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"" |