您的位置:首页 > 编程语言 > Python开发

Python中subprocess学习

2015-07-14 15:23 561 查看
控制脚本执行时间方法:

你也可以定时poll, 超时后,直接杀死;你可以监听sigchld信号,然后去poll, 如果超时,还没有sigchld信号,直接杀死

import subprocess

import time

from _common.util import *

def cmd_run(cmd, timeout=5):

proc = subprocess.Popen(cmd,

stdin=subprocess.PIPE,
stdout=subprocess.PIPE,

stderr=subprocess.PIPE,

shell=True)

poll_seconds = 1

deadline = time.time() + timeout

while time.time() < deadline and proc.poll() == None:

time.sleep(poll_seconds)

if proc.poll() == None:

proc.kill()

#stdout, stderr = proc.communicate()

return proc.returncode

while True:

try:

cmd = 'python %s/script.py lvs health_check' % script_dir()

retcode = cmd_run(cmd,5)

time.sleep(1)

print retcode

except Exception:

print 'Run command error'

else:

print retcode

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。

---------------------------------------------------------------

简单的用法:

[python]
view plaincopy

p=subprocess.Popen("dir", shell=True)
p.wait()

shell参数根据你要执行的命令的情况来决定,上面是dir命令,就一定要shell=True了,p.wait()可以得到命令的返回值。

如果上面写成a=p.wait(),a就是returncode。那么输出a的话,有可能就是0【表示执行成功】。

---------------------------------------------------------------------------

进程通讯

如果想得到进程的输出,管道是个很方便的方法,这样:

[python]
view plaincopy

p=subprocess.Popen("dir", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(stdoutput,erroutput) = p.<span>commu</span>nicate()

p.communicate会一直等到进程退出,并将标准输出和标准错误输出返回,这样就可以得到子进程的输出了。

再看一个communicate的例子。



上面的例子通过communicate给stdin发送数据,然后使用一个tuple接收命令的执行结果。

------------------------------------------------------------------------

上面,标准输出和标准错误输出是分开的,也可以合并起来,只需要将stderr参数设置为subprocess.STDOUT就可以了,这样子:

[python]
view plaincopy

p=subprocess.Popen("dir", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
(stdoutput,erroutput) = p.<span>commu</span>nicate()

如果你想一行行处理子进程的输出,也没有问题:

[python]
view plaincopy

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

------------------------------------------------------

今天遇到的一个问题。简单说就是,使用
subprocess
模块的
Popen
调用外部程序,如果
stdout
stderr
参数是 pipe,并且程序输出超过操作系统的 pipe size时,如果使用
Popen.wait()
方式等待程序结束获取返回值,会导致死锁,程序卡在
wait()
调用上。

ulimit -a
看到的 pipe size 是 4KB,那只是每页的大小,查询得知 linux 默认的
pipe size 是 64KB。

看例子:

#!/usr/bin/env python
# coding: utf-8
# yc@2013/04/28

import subprocess

def test(size):
print 'start'

cmd = 'dd if=/dev/urandom bs=1 count=%d 2>/dev/null' % size
p = subprocess.Popen(args=cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True)
#p.communicate()
p.wait()

print 'end'

# 64KB
test(64 * 1024)

# 64KB + 1B
test(64 * 1024 + 1)

首先测试输出为 64KB 大小的情况。使用 dd 产生了正好 64KB 的标准输出,由
subprocess.Popen
调用,然后使用
wait()
等待
dd
调用结束。可以看到正确的
start
end
输出;然后测试比 64KB 多的情况,这种情况下只输出了
start
,也就是说程序执行卡在了
p.wait()
上,程序死锁。具体输出如下:

start
end
start

那死锁问题如何避免呢?官方文档里推荐使用
Popen.communicate()
。这个方法会把输出放在内存,而不是管道里,所以这时候上限就和内存大小有关了,一般不会有问题。而且如果要获得程序返回值,可以在调用
Popen.communicate()
之后取
Popen.returncode
的值。

结论:如果使用
subprocess.Popen
,就不使用
Popen.wait()
,而使用
Popen.communicate()
来等待外部程序执行结束。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: