一、os

以前我一直用os.system()处理一些系统管理任务,因为我认为那是运行linux命令或win命令最简单的方式.。在Windows上也可以使用os.system()这个函数来执行一些dos命令,但是这个命令只能拿到返回码,拿不到标准输出,标准错误,所以通常使用的subprocess模块中的Popen类来实现。

1.1 os下的命令执行模块

函数名

描述

os.system(command)

返回命令执行状态码,而将命令执行结果输出到屏幕

os.popen(command).read()

可以获取命令执行结果,但是无法获取命令执行状态码

commands.getstatusoutput(command)

返回一个元组(命令执行状态码, 命令执行结果)

说明:

  1. os.popen(command)函数得到的是一个文件对象,因此除了read()方法外还支持write()等方法,具体要根据command来定;
  2. 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()。

说明:

  1. 在Python 3.5之后的版本中,官方文档中提倡通过subprocess.run()函数替代其他函数来使用subproccess模块的功能;
  2. 在Python 3.5之前的版本中,我们可以通过subprocess.call(),subprocess.getoutput()等上面列出的其他函数来使用subprocess模块的功能;
  3. subprocess.run()、subprocess.call()、subprocess.check_call()和subprocess.check_output()都是通过对subprocess.Popen的封装来实现的高级函数,因此如果我们需要更复杂功能时,可以通过subprocess.Popen来完成。
  4. 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",但并不推荐这样的方式.
如果你知道你只用几个有限的函数,比如PopenPIPE,你可以单单指定这几个函数:

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()

三、总结

  1. Python2.4版本引入了subprocess模块用来替换os.system()、os.popen()、os.spawn*()等函数以及commands模块;也就是说如果你使用的是Python 2.4及以上的版本就应该使用subprocess模块了。
  2. 如果你的应用使用的Python 2.4以上,但是是Python 3.5以下的版本,Python官方给出的建议是使用subprocess.call()函数。Python 2.5中新增了一个subprocess.check_call()函数,Python 2.7中新增了一个subprocess.check_output()函数,这两个函数也可以按照需求进行使用。
  3. 如果你的应用使用的是Python 3.5及以上的版本,Python官方给出的建议是尽量使用subprocess.run()函数
  4. 当subprocess.call()、subprocess.check_call()、subprocess.check_output()和subprocess.run()这些高级函数无法满足需求时,我们可以使用subprocess.Popen类来实现我们需要的复杂功能。