您的位置:首页 > 运维架构

Python_cmd的各种实现方法及优劣(subprocess.Popen, os.system和commands.getstatusoutput)

2015-10-13 16:15 771 查看
一、目前我使用到的python中执行cmd的方式有三种:

1. 使用os.system("cmd")
这是最简单的一种方法,特点是执行的时候程序会打出cmd在linux上执行的信息。使用前需要import os。
[python] view plaincopyprint?
os.system("ls")
2. 使用Popen模块产生新的process
现在大部分人都喜欢使用Popen。Popen方法不会打印出cmd在linux上执行的信息。的确,Popen非常强大,支持多种参数和模式。使
用前需要from subprocess import Popen,
PIPE。但是Popen函数有一个缺陷,就是它是一个阻塞的方法。如果运行cmd时产生的内容非常多,函数非常容易阻塞住。解决办法是不使用
wait()方法,但是也不能获得执行的返回值了。
Popen原型是:
[python] view plaincopyprint?
subprocess.Popen(args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, shell=False, cw
参数:
bufsize:指定缓冲,
executable:用于指定可执行程序。一般情况下我们通过args参数来设置所要运行的程序。如果将参数shell设为True,executable 将指定程序使用的shell.在windows平台中默认的 shell由COMSPEC环境变量来指定。
stdin,stdout,stderr分别表示程序的标准输入、输出、错误句柄。他们可以是PIPE,文件描述符或文件对象,也可以设置为None,表示从父进程继承。
preexec_fn只在Uinx平台下有效,用于指定一个可执行对象(callable object),它将在子进程运行之前被调用。
Close_sfs:在windows平台下,如果close_fds被设置为True,则新创建的子进程将不会继承父进程的输入、输出、错误管道。我们不能将close_fds设置为True同时重定向子进程的标准输入、输出与错误(stdin, stdout, stderr)。
shell 设置为True,程序将通过shell来执行。
cwd 用于设置子进程的当前目录。
env 时字典类型,用于指定子进程的环境变量。env=None 子进程的环境变量将从父进程中继承。
Universal_newlines:不同操作系统下,文本的换行符是不一样的。将此参数设置为True Python统一把这些换行符当做'/n'来处理。
startupinfo 与 createionflags 只是在windows下有效。

subprocess.PIPE
在创建Popen对象时,subprocess.PIPE可以初始化 stdin,stdout或stderr参数,表示与子进程通信的标准流。
subprocess.STDOUT
创建Popen对象时,用于初始化stderr参数,表示将错误通过标准输出流输出。
Popen 的方法:
Popen.poll() 用于检查子进程是否已经结束。设置并返回returncode属性。
Popen.communicate(input=None)
与子进程进行交互。向stdin发送数据,或从stdout和stderr中读取数据。可选参数input指定发送到子进程的参数。Communicate()返回一个元组:(stdoutdata,stderrdata)。注意:如果希望通过进程的stdin向其发送数据,在创建Popen对象的时候,参数stdin必须被设置为PIPE。同样,如 果希望从stdout和stderr获取数据,必须将stdout和stderr设置为PIPE。
Popen.send_signal(signal)
向子进程发送信号
Popen.terminate()
停止(stop)子进程。在windows平台下,该方法将调用Windows API TerminateProcess()来结束子进程。
Popen.kill()
杀死子进程

Popen.stdin
如果在创建Popen对象是,参数stdin被设置为PIPE,Popen.stdin将返回一个文件对象用于策子进程发送指令。否则返回None。Popen.stdout
如果在创建Popen对象是,参数stdout被设置为PIPE,Popen.stdout将返回一个文件对象用于策子进程发送指令。否则返回 None。Popen.stderr
如果在创建Popen对象是,参数stdout被设置为PIPE,Popen.stdout将返回一个文件对象用于策子进程发送指令。否则返回 None。Popen.pid
获取子进程的进程ID。Popen.returncode
获取进程的返回值。如果进程还没有结束,返回None。
p = Popen("cp -rf a/* b/", shell=True, stdout=PIPE, stderr=PIPE)
p.wait()
if p.returncode != 0:
print "Error."
return -1
3. 使用commands.getstatusoutput方法这个方法也不会打印出cmd在linux上执行的信息。这个方法唯一的优点是,它不是一个阻塞的方法。即没有Popen函数阻塞的问题。使用前需要import commands。
例如:

status, output = commands.getstatusoutput(
"ls"
)


还有只获得output和status的方法:
commands.getoutput("ls")
commands.getstatus("ls")


二、Python中subprocess学习
subprocess的目的就是启动一个新的进程并且与之通信。
subprocess 模块中之定义了一个类:Popen。 可以使用 Popen来创建进程,并与进程进行复杂的交互。
subprocess.Popen(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可以是字符串或者序列类型(如:list,元组),用于指定进程的可执行文件及其参数。如果是序列类型,第一个元素通常是可执行文件的路径。我们也可以显式的使用executeable参数来指定可执行文件的路径。
参数stdin, stdout, stderr分别表示程序的标准输入、输出、错误句柄。他们可以是PIPE,文件描述符或文件对象,也可以设置为None,表示从父进程继承。
如果参数shell设为true,程序将通过shell来执行。
参数env是字典类型,用于指定子进程的环境变量。如果env = None,子进程的环境变量将从父进程中继承。
subprocess.PIPE
  在创建Popen对象时,subprocess.PIPE可以初始化stdin, stdout或stderr参数。表示与子进程通信的标准流。
subprocess.STDOUT
  创建Popen对象时,用于初始化stderr参数,表示将错误通过标准输出流输出。
Popen的方法:
Popen.poll()
  用于检查子进程是否已经结束。设置并返回returncode属性。
Popen.wait()
  等待子进程结束。设置并返回returncode属性。
Popen.communicate(input=None)
  与子进程进行交互。向stdin发送数据,或从stdout和stderr中读取数据。可选参数input指定发送到子进程的参数。Communicate()返回一个元组:(stdoutdata, stderrdata)。注意:如果希望通过进程的stdin向其发送数据,在创建Popen对象的时候,参数stdin必须被设置为PIPE。同样,如果希望从stdout和stderr获取数据,必须将stdout和stderr设置为PIPE。
Popen.send_signal(signal)
  向子进程发送信号。
Popen.terminate()
  停止(stop)子进程。在windows平台下,该方法将调用Windows API TerminateProcess()来结束子进程。
Popen.kill()
  杀死子进程。
Popen.stdin,Popen.stdout ,Popen.stderr ,官方文档上这么说:
stdin, stdout and stderr specify
the executed programs’ standard input, standard output and standard error file handles, respectively. Valid values are PIPE,
an existing file descriptor (a positive integer), an existing file object, and None.

Popen.pid
  获取子进程的进程ID。
Popen.returncode
  获取进程的返回值。如果进程还没有结束,返回None。

简单的用法:
p=subprocess.Popen("dir", shell=True)
p.wait()
shell参数根据你要执行的命令的情况来决定,上面是dir命令,就一定要shell=True了,p.wait()可以得到命令的返回值。
如果上面写成a=p.wait(),a就是returncode。那么输出a的话,有可能就是0【表示执行成功】。

进程通讯
p=subprocess.Popen("dir", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(stdoutput,erroutput) = p.<span>commu</span>nicate()
如果你想一行行处理子进程的输出,也没有问题:
p=subprocess.Popen("dir", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
while True:
buff = p.stdout.readline()
if buff == '' and p.poll() != None:
break
死锁
但是如果你使用了管道,而又不去处理管道的输出,那么小心点,如果子进程输出数据过多,死锁就会发生了,比如下面的用法:
p=subprocess.Popen("longprint", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
p.wait()
longprint是一个假想的有大量输出的进程,那么在我的xp, Python2.5的环境下,当输出达到4096时,死锁就发生了。当然,如果我们用p.stdout.readline或者p.communicate去清理输出,那么无论输出多少,死锁都是不会发生的。或者我们不使用管道,比如不做重定向,或者重定向到文件,也都是可以避免死锁的

subprocess还可以连接起来多个命令来执行。
在shell中我们知道,想要连接多个命令可以使用管道。
在subprocess中,可以使用上一个命令执行的输出结果作为下一次执行的输入
cat ff
this is a test
this is a test
this is a test
this is a test

p1 = subprocess.Popen('cat ff',shell=True,stdout=subprocess.PIPE)
p2 = subprocess.Popen('tail -2',shell = True,stdin=p1.stdout,stdout=subprocess.PIPE)
print p2.stdout.read()
this is a test
this is a test
例子中,p2使用了第一次执行命令的结果p1的stdout作为输入数据,然后执行tail命令。

下面是一个更大的例子。用来ping一系列的ip地址,并输出是否这些地址的主机是alive的。代码参考了python unix linux 系统管理指南。
#!/usr/bin/env python
# coding=utf-8
__author__ = 'admin'

from threading import Thread
import subprocess
from Queue import Queue

num_threads = 3
ips = ['192.168.49.85','192.168.49.121']
q = Queue()

def pingme(i,queue):
while True:
ip = queue.get()
print('Thread %s pinging %s' %(i,ip))
ret = subprocess.call('ping -c 1 %s' % ip,shell=True,stdout=open('/dev/null','w'),stderr=subprocess.STDOUT)
if ret == 0:
print('%s is alive!'%ip)
else:
print('%s is down!'%ip)
queue.task_done()

for i in range(num_threads):
t = Thread(target=pingme,args=(i,q))
t.setDaemon(True)
t.start()
for ip in ips:
q.put(ip)
print('main thread waiting...')
q.join();print('Done')


pingme函数的执行是这样的:
启动的线程会去执行pingme函数。
pingme函数会检测队列中是否有元素。如果有的话,则取出并执行ping命令。
这个队列是多个线程共享的。所以这里我们不使用列表。【假设在这里我们使用列表,那么需要我们自己来进行同步控制。Queue本身已经通过信号量做了同步控制,节省了我们自己做同步控制的工作=。=】
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: