python调用外部子进程,通过管道实现异步标准输入和输出的交互

我们通常会遇到这样的需求:通过C++或其他较底层的语言实现了一个复杂的功能模块,需要搭建一个基于Web的Demo,方法查询数据。由于Python语言的强大和简洁,其用来搭建Demo非常合适,Flask框架和jinja2模块功能为python提供了方便的web开发能力。同时,python能够很方便的同其他语言的代码交互。因此我们选择python作为开发Demo的工具。假设我们需要调用的模块(提供底层服务)通过标准输入循环读入数据,处理完毕后把结果写出到标出输出,这样的场景在Linux环境下很常见,依赖于Linux强大的重定向能力。然而,非常不幸的是,底层模块有一个很重的初始化过程,因此我们不能够每次查询请求都去重新生成调用底层模块的子进程。解决方案就是只生成一次子进程,然后对每个请求通过管道(pipe)来和子进程交互。

Python的subprocess模块可以很容易地生成子进程,类似Linux系统调用fork和exec。subprocess模块的Popen对象可能以非阻塞的方式调用外部可执行程序,因此我们使用Poen对象来实现需求。如果我们想要把数据写入子进程的标准输入stdin,那么在创建Popen对象的时候就需要指定参数stdin为subprocess.PIPE;同样,如果我们需要从子进程的标准输出中读取数据,那么在创建Popen对象的时候就需要指定参数stdout为subprocess.PIPE。先看一个简单的例子:


from subprocess import Popen, PIPE

p = Popen(‘less‘, stdin=PIPE, stdout=PIPE)
p.communicate(‘Line number %d.\n‘ % x)

communicate函数返回一个二元组(stdoutdata, stderrdata),包含了子进程的标准输出和标出错误的输出数据。然而,由于Popen对象的communicate函数会阻塞父进程,同时还会关闭管道,因此每个Popen对象只能调用一次communicate函数,如果有多个请求必须重新生成Popen对象(重新初始化子进程),不能满足我们的需求。

因此,我们只有往Popen对象的stdin和stdout对象里写入和读取数据才能实现我们的需求。然而,不幸的是subprocess模块默认情况下只运行在子进程结束的时候读取一次标准输出。Both subprocess and os.popen* only allow input and
output one time, and the output to be read only when the process
terminates

进过一番研究之后我发现通过fcntl模块的fcntl函数可以把子进程的标准输出改为非阻塞的方式,从而达到我们的目的。这样困扰我许久的问题终于得到了完美解决。代码如下:


#!/usr/bin/python
# -*- coding: utf-8 -*-
# author: [email protected]
from subprocess import Popen, PIPE
import fcntl, os
import time

class Server(object):
def __init__(self, args, server_env = None):
if server_env:
self.process = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE, env=server_env)
else:
self.process = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE)
flags = fcntl.fcntl(self.process.stdout, fcntl.F_GETFL)
fcntl.fcntl(self.process.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK)

def send(self, data, tail = ‘\n‘):
self.process.stdin.write(data + tail)
self.process.stdin.flush()

def recv(self, t=.1, e=1, tr=5, stderr=0):
time.sleep(t)
if tr < 1:
tr = 1
x = time.time()+t
r = ‘‘
pr = self.process.stdout
if stderr:
pr = self.process.stdout
while time.time() < x or r:
r = pr.read()
if r is None:
if e:
raise Exception(message)
else:
break
elif r:
return r.rstrip()
else:
time.sleep(max((x-time.time())/tr, 0))
return r.rstrip()

if __name__ == "__main__":
ServerArgs = [‘/home/weisu.yxd/QP/trunk/bin/normalizer‘, ‘/home/weisu.yxd/QP/trunk/conf/stopfile.txt‘]
server = Server(ServerArgs)
test_data = ‘在云端‘, ‘云梯‘, ‘摩萨德‘, ‘Alisa‘, ‘iDB‘, ‘阿里大数据‘
for x in test_data:
server.send(x)
print x, server.recv()

另外,调用一些外部程序时,可能需要指定相应的环境变量,方式如下:


  my_env = os.environ
my_env["LD_LIBRARY_PATH"] = "/path/to/lib"
server = server.Server(cmd, my_env)

时间: 2024-10-01 20:03:46

python调用外部子进程,通过管道实现异步标准输入和输出的交互的相关文章

Python调用外部系统命令

利用Python调用外部系统命令的方法可以提高编码效率.调用外部系统命令完成后可以通过获取命令执行返回结果码.执行的输出结果进行进一步的处理.本文主要描述Python常见的调用外部系统命令的方法,包括os.system().os.popen().subprocess.Popen()等. 1.subprocess模块 优先介绍subprocess模块的是由于该模块可以替代旧模块的方法,如os.system().os.popen()等,推荐使用.subporcess模块可以调用外部系统命令来创建新子

Python 调用外部命令

python 可以使用 os 模块来调用外部的 Linux Shell 命令,常用的方法如下: (1) os.system():结果输出在终端上,捕获不到(2) os.popen() : 结果返回一个对象,即标准输出(3) os.popen2():结果返回两个对象,分别是标准输入,标准输出(4) os.popen3():结果返回三个对象,分别是标准输入,标准输出,标准错误输出(5) os.popen4():结果返回两个对象,分别是标准输入,标准输出(标准输出中包括标准错误输出) In [2]:

python调用外部命令

os.system:  输出在终端上,捕捉不到 os.popen:  只能捕捉到标准输出,捕捉不到标准错误输出 os.popen2: 返回2个对象,一个是标准输入,一个是标准输出 os.popen3: 返回3个对象,标准输入,标准输出,标准错误输出 os.popen4: 返回2个对象,pip_in 和pip_out_err os.system:  输出在终端上,捕捉不到 In [4]: os.system('ls')                                        

21 python调用外部系统命令

1 Python中执行系统命令常见的几种方法 2 一,使用os模块 3 import os 4 1,os.system 5 6 import os 7 os.system('cat /proc/cupinfo') ## 类似于perl中的system命令,不能获取命令返回结果,只返回成功或失败 8 注意: 9 本质是调用标准C的system() 函数,仅仅在一个子终端运行系统命令, 10 ##不能获取命令执行后的返回信息. 11 12 2, os.popen ##相当于perl中的readpip

python模拟鼠标键盘操作 GhostMouse tinytask 调用外部脚本或程序 autopy右键另存为

1.参考 autopy (实践见最后一章节) 用Python制作游戏外挂(上) AutoPy Introduction and Tutorial autopy.mouse.smooth_move(1, 1) 可以实现平滑移动 autopy - API Reference pip install PyUserInput SavinaRoja/PyUserInput [python3.5][PyUserInput]模拟鼠标和键盘模拟 Python-模拟鼠标键盘动作 autoit selenium借助

Zabbix调用外部脚本发送邮件:python编写脚本

Zabbix调用外部脚本发送邮件的时候,会在命令行传入两个参数,第一个参数就是要发送给哪个邮箱地址,第二个参数就是邮件信息,为了保证可以传入多个参数,所以假设有多个参数传入 #!/usr/bin/env python #encoding:utf8 # # Zabbix Server 发送邮件脚本 # from email import encoders from email.header import Header from email.mime.text import MIMEText fro

Python调用C可执行程序(subprocess)

从Python 2.4开始,Python引入subprocess模块来管理子进程,以取代一些旧模块的方法:如 os.system.os.spawn.os.popen.popen2.commands. 不但可以调用外部的命令作为子进程,而且可以连接到子进程的input/output/error管道,获取相关的返回信息. subprocess包中定义有数个创建子进程的函数,这些函数分别以不同的方式创建子进程,所以我们可以根据需要来从中选取一个使用.另外subprocess还提供了一些管理标准流(st

python 调用shell命令三种方法

#!/usr/bin/python是告诉操作系统执行这个脚本的时候,调用/usr/bin下的python解释器: #!/usr/bin/env python这种用法是为了防止操作系统用户没有将python装在默认的/usr/bin路径里. python调用shell命令的方法有许多 1.1   os.system(command) 在一个子shell中运行command命令,并返回command命令执行完毕后的退出状态.这实际上是使用C标准库函数system()实现的.这个函数在执行comman

python调用shell命令

1.1   os.system(command) 在一个子shell中运行command命令,并返回command命令执行完毕后的退出状态.这实际上是使用C标准库函数system()实现的.这个函数在执行command命令时需要重新打开一个终端,并且无法保存command命令的执行结果. 1.2   os.popen(command,mode) 打开一个与command进程之间的管道.这个函数的返回值是一个文件对象,可以读或者写(由mode决定,mode默认是'r').如果mode为'r',可以