一、os
以前我一直用os.system()
处理一些系统管理任务,因为我认为那是运行linux命令或win命令最简单的方式.。在Windows上也可以使用os.system()这个函数来执行一些dos命令,但是这个命令只能拿到返回码,拿不到标准输出,标准错误,所以通常使用的subprocess模块中的Popen类来实现。
1.1 os下的命令执行模块
函数名 | 描述 |
os.system(command) | 返回命令执行状态码,而将命令执行结果输出到屏幕 |
os.popen(command).read() | 可以获取命令执行结果,但是无法获取命令执行状态码 |
commands.getstatusoutput(command) | 返回一个元组(命令执行状态码, 命令执行结果) |
说明:
- os.popen(command)函数得到的是一个文件对象,因此除了read()方法外还支持write()等方法,具体要根据command来定;
- commands模块只存在于Python 2.7中,且不支持windows平台,因此commands模块很少被使用。另外,commands模块实际上也是通过对os.popen()的封装来完成的。
函数名及描述:
os.system(command) 返回命令执行状态码,而将命令执行结果输出到屏幕;
os.popen(command).read() 可以获取命令执行结果,但是无法获取命令执行状态码;
commands.getstatusoutput(command) 返回一个元组(命令执行状态码, 命令执行结果);
os.popen(command)函数得到的是一个文件对象,因此除了read()方法外还支持write()等方法,具体要根据command来定;
commands模块只存在于Python 2.7中,且不支持windows平台,因此commands模块很少被使用。另外,commands模块实际上也是通过对os.popen()的封装来完成的。
示例:
import os
retcode = os.system('dir')
import os
ret = os.popen('dir').read()
print(ret)
需要注意的是commands模块不支持windows平台,因此该实例是在Linux平台下执行的
import commands
retcode, ret = commands.getstatusoutput('ls -l')
retcode
0
print(ret)
通过查看commands模块提供的属性可知,它也提供了单独获取命令执行状态码和执行结果的函数,如下所示:
dir(commands)
['__all__', '__builtins__', '__doc__', '__file__', '__name__', '__package__', 'getoutput', 'getstatus', 'getstatusoutput', 'mk2arg', 'mkarg']
早期的Python版本中,我们主要是通过os.system()、os.popen().read()等函数来执行命令行指令的,另外还有一个很少使用的commands模块。但是从Python 2.4开始官方文档中建议使用的是subprocess模块,所以os模块和commands模块的相关函数在这里只提供一个简单的使用示例,我们重要要介绍的是subprocess模块。
二、 subprocess
我们能从Python官方文档里读到应该用subprocess
模块来运行系统命令。subprocess
模块允许我们创建子进程,连接他们的输入/输出/错误管道,还有获得返回值。subprocess
模块打算来替代几个过时的模块和函数,比如:os.system, os.spawn*, os.popen*, popen2.*
命令。
DESCRIPTION
This module allows you to spawn processes, connect to their
input/output/error pipes, and obtain their return codes.
即允许你去创建一个新的进程让其执行另外的程序,并与它进行通信,获取标准的输入、标准输出、标准错误以及返回码等。
subprocess – 创建附加进程 ,subprocess是Python 2.4中新增的一个模块,它允许你生成新的进程,连接到它们的 input/output/error 管道,并获取它们的返回(状态)码。
subprocess模块提供了一种一致的方法来创建和处理附加进程,与标准库中的其它模块相比,提供了一个更高级的接口。用于替换如下模块:
os.system() , os.spawnv() , os和popen2模块中的popen()函数,以及 commands().
2.1 函数介绍
让我们来看一下subprocess
有哪些不同的函数:
函数 | 描述 |
subprocess.run() | Python 3.5中新增的函数。执行指定的命令,等待命令执行完成后返回一个包含执行结果的CompletedProcess类的实例。 |
subprocess.call() | 执行指定的命令,返回命令执行状态,其功能类似于os.system(cmd)。 |
subprocess.check_call() | Python 2.5中新增的函数。 执行指定的命令,如果执行成功则返回状态码,否则抛出异常。其功能等价于subprocess.run(..., check=True)。 |
subprocess.check_output() | Python 2.7中新增的的函数。执行指定的命令,如果执行状态码为0则返回命令执行结果,否则抛出异常。 |
subprocess.getoutput(cmd) | 接收字符串格式的命令,执行命令并返回执行结果,其功能类似于os.popen(cmd).read()和commands.getoutput(cmd)。 |
subprocess.getstatusoutput(cmd) | 执行cmd命令,返回一个元组(命令执行状态, 命令执行结果输出),其功能类似于commands.getstatusoutput()。 |
说明:
- 在Python 3.5之后的版本中,官方文档中提倡通过subprocess.run()函数替代其他函数来使用subproccess模块的功能;
- 在Python 3.5之前的版本中,我们可以通过subprocess.call(),subprocess.getoutput()等上面列出的其他函数来使用subprocess模块的功能;
- subprocess.run()、subprocess.call()、subprocess.check_call()和subprocess.check_output()都是通过对subprocess.Popen的封装来实现的高级函数,因此如果我们需要更复杂功能时,可以通过subprocess.Popen来完成。
- subprocess.getoutput()和subprocess.getstatusoutput()函数是来自Python 2.x的commands模块的两个遗留函数。它们隐式的调用系统shell,并且不保证其他函数所具有的安全性和异常处理的一致性。另外,它们从Python 3.3.4开始才支持Windows平台。
subprocess.call()
执行由参数提供的命令。我们可以用数组作为参数运行命令,也可以用字符串作为参数运行命令(通过设置参数shell=True)
注意,参数shell默认为False。
我们用subprocess.call()来做一个统计磁盘的例子:
subprocess.call(['df', '-h'])
下面的例子把shell设置为True
subprocess.call('du -hs $HOME', shell=True)
注意,python官方文档里对参数shell=True陈述了一个警告:
Invoking the system shell with shell=True can be a security hazard if combined
with untrusted input
现在,我们来看看输入与输出
Input and Output
subprocess模块能阻止输出,当你不关心标准输出的时候是非常方便的.
它也使你通过一种正确的方式管理输入/输出,有条理地整合python脚本中的的shell命令。
Return Codes
通过subprocess.call的返回值你能够判定命令是否执行成功.
每一个进程退出时都会返回一个状态码,你可以根据这个状态码写一些代码。
stdin, stdout and stderr
我在使用subprocess时,有一个微妙的部分是怎么使用管道把命令连接起来.
管道表明一个新的子管道应该被创建。
默认的设置为None,意味着没有重定向发生
标准错误可以指向标准输出,表明子进程的错误信息会被捕获到和标准输出同一个文件。
subprocess.Popen()
subprocess模块中基本的进程创建和管理由Popen类来处理.
subprocess.popen是用来替代os.popen的.
我们来做一些真实的例子,subprocess.Popen需要一个数组作为参数:
import subprocess
p = subprocess.Popen(["echo", "hello world"], stdout=subprocess.PIPE)print p.communicate()
>>>('hello world
', None)
注意,虽然你可以使用 "shell=True",但并不推荐这样的方式.
如果你知道你只用几个有限的函数,比如Popen
和PIPE
,你可以单单指定这几个函数:
from subprocess import Popen, PIPEp1 = Popen(["dmesg"], stdout=PIPE)print p1.communicate()
Popen.communicate()
communicate()函数返回一个tuple(标准输出和错误)。
Popen.communicate()和进程沟通:发送数据到标准输入。从标准输出和错误读取数据直到遇到结束符。等待进程结束。
输入参数应该是一个字符串,以传递给子进程,如果没有数据的话应该是None.
基本上,当你用communicate()函数的时候意味着你要执行命令了.
2.2 详细用法
Popen类
subprocess模块中定义了一个Popen类,通过它可以来创建进程,并与其进行复杂的交互。查看一下它的构造函数:
__init__(self, args, bufsize=0, executable=None,
stdin=None, stdout=None, stderr=None, preexec_fn=None,
close_fds=False, shell=False, cwd=None, env=None,
universal_newlines=False, startupinfo=None,
creationflags=0)
args:args should be a string, or a sequence of program arguments.也就是说必须是一个字符串或者序列类型(如:字符串、list、元组),用于指定进程的可执行文件及其参数。如果是一个序列类型参数,则序列的第一个元素通常都必须是一个可执行文件的路径。当然也可以使用executeable参数来指定可执行文件的路径。
stdin,stdout,stderr:分别表示程序的标准输入、标准输出、标准错误。有效的值可以是PIPE,存在的文件描述符,存在的文件对象或None,如果为None需从父进程继承过来,stdout可以是PIPE,表示对子进程创建一个管道,stderr可以是STDOUT,表示标准错误数据应该从应用程序中捕获并作为标准输出流stdout的文件句柄。
shell:如果这个参数被设置为True,程序将通过shell来执行。
env:它描述的是子进程的环境变量。如果为None,子进程的环境变量将从父进程继承而来。
创建Popen类的实例对象
res = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
cmd:标准像子进程传入需要执行的shell命令,如:ls -al
subprocess.PIPE:在创建Popen对象时,subprocess.PIPE可以初始化为stdin, stdout或stderr的参数,表示与子进程通信的标准输入流,标准输出流以及标准错误。
subprocess.STDOUT:作为Popen对象的stderr的参数,表示将标准错误通过标准输出流输出。
Popen类拥有的方法及属性
1、Popen.pid
获取子进程的进程ID。
2、Popen.returncode
获取进程的返回码。如果进程未结束,将返回None。
3、communicate(input=None)
官方解释:
Interact with process: Send data to stdin. Read data from
stdout and stderr, until end-of-file is reached. Wait for
process to terminate. The optional input argument should be a
string to be sent to the child process, or None, if no data
should be sent to the child.communicate() returns a tuple (stdout, stderr).
与子进程进行交互,像stdin发送数据,并从stdout和stderr读出数据存在一个tuple中并返回。
参数input应该是一个发送给子进程的字符串,如果未指定数据,将传入None。
4、poll()
检查子进程是否结束,并返回returncode属性。
5、wait()
Wait
for
child process to terminate. Returns returncode attribute.
等待子进程执行结束,并返回returncode属性,如果为0表示执行成功。
6、send_signal( sig)
Send a signal to the process.
发送信号给子进程。
7、terminate()
Terminates the process.
终止子进程。windows下将调用Windows API TerminateProcess()来结束子进程。
8、kill()
官方文档对这个函数的解释跟terminate()是一样的,表示杀死子进程。
进程通信实例1
打开一个只有ip地址的文本文件,读取其中的ip,然后进行ping操作,并将ping结果写入ping.txt文件中。
首先创建一个子进程res,传入要执行的shell命令,并获得标准输出流、返回码等。
import subprocess
import os
class Shell(object) :
def runCmd(self, cmd) :
res = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
sout ,serr = res.communicate()
return res.returncode, sout, serr, res.pid
shell = Shell()
fp = open('c:\\test\\ip.txt', 'r')
ipList = fp.readlines()
fp.close()
fp = open('c:\\test\\ping.txt', 'a')
print ipList
for i in ipList :
i = i.strip()
result = shell.runCmd('ping ' + i)
if result[0] == 0 :
w = i + ' : 0'
fp.write(w + '\n')
else :
w = i + ' : 1'
fp.write(w + '\n')
fp.close()
三、总结
- Python2.4版本引入了subprocess模块用来替换os.system()、os.popen()、os.spawn*()等函数以及commands模块;也就是说如果你使用的是Python 2.4及以上的版本就应该使用subprocess模块了。
- 如果你的应用使用的Python 2.4以上,但是是Python 3.5以下的版本,Python官方给出的建议是使用subprocess.call()函数。Python 2.5中新增了一个subprocess.check_call()函数,Python 2.7中新增了一个subprocess.check_output()函数,这两个函数也可以按照需求进行使用。
- 如果你的应用使用的是Python 3.5及以上的版本,Python官方给出的建议是尽量使用subprocess.run()函数。
- 当subprocess.call()、subprocess.check_call()、subprocess.check_output()和subprocess.run()这些高级函数无法满足需求时,我们可以使用subprocess.Popen类来实现我们需要的复杂功能。