2012-01-23 87 views
10

有没有办法在python“持久化”中进行子进程调用?我正在调用需要一段时间才能加载多次的程序。所以,如果我能将这个程序打开并与它通信而不会造成它的损坏,那将是非常好的。持久性python子进程

我的Python脚本的卡通版本是这样的:

for text in textcollection: 
    myprocess = subprocess.Popen(["myexecutable"], 
       stdin = subprocess.PIPE, stdout = subprocess.PIPE, 
       stderr = None) 
    myoutputtext, err = myprocess.communicate(input=text) 

我需要分别处理每个文本,所以它所有加入到一个大的文本文件,一旦处理它不是一个选项。

最好,如果有这样的

myprocess = subprocess.Popen(["myexecutable"], 
      stdin = subprocess.PIPE, stdout = subprocess.PIPE, 
      stderr = None) for text in textcollection: 
for text in textcollection: 
    myoutputtext, err = myprocess.communicate(input=text) 

一种选择,我可以离开过程公开,我会很感激。

回答

24

你可以使用myprocess.stdin.write()myprocess.stdout.read()与您的子进程进行通信,您只需要小心,以确保正确处理缓冲以防止阻止您的呼叫。

如果您的子过程的输出定义良好,您应该能够使用行缓冲和myprocess.stdout.readline()进行可靠的通信。

下面是一个例子:

>>> p = subprocess.Popen(['cat'], bufsize=1, stdin=subprocess.PIPE, stdout=subprocess.PIPE) 
>>> p.stdin.write('hello world\n') 
>>> p.stdout.readline() 
'hello world\n' 
>>> p.stdout.readline()  # THIS CALL WILL BLOCK 

这种方法适用于Unix的替代方法是把文件句柄在非阻塞模式,这将使你打电话的功能,如myprocess.stdout.read()并有如果返回数据任何可用,或引发IOError如果没有任何数据:

>>> p = subprocess.Popen(['cat'], stdin=subprocess.PIPE, stdout=subprocess.PIPE) 
>>> import fcntl, os 
>>> fcntl.fcntl(p.stdout.fileno(), fcntl.F_SETFL, os.O_NONBLOCK) 
0 
>>> p.stdout.read()   # raises an exception instead of blocking 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
IOError: [Errno 11] Resource temporarily unavailable 

这将允许你做这样的事情:

fcntl.fcntl(p.stdout.fileno(), fcntl.F_SETFL, os.O_NONBLOCK) 
for text in textcollection: 
    myprocess.stdin.write(text + '\n') 
    while True: 
     myoutputtext = '' 
     try: 
      myoutputtext += myprocess.stdout.read() 
     except IOError: 
      pass 
     if validate_output(myoutputtext): 
      break 
     time.sleep(.1) # short sleep before attempting another read 

在本例中,validate_output()是您需要编写的函数,如果您迄今为止收到的数据全部是您期望得到的输出,则返回True

+1

谢谢!我最喜欢你的解决方案,因为它不需要第三方下载。不幸的是,它不适合我。在尝试了几件事情之后,我很确定这是我调用的Java程序而不是您的解决方案的问题,因此您的解决方案很好。 – JasonMond

+0

为什么选择投票? –

+0

这是错误的。我的upvote是不活动的,直到编辑任何东西,但我没有看到任何改善或不受伤害的东西。完美的答案。 – hynekcer

1

我认为你正在寻找

myprocess.stdin.write(text) 

你可以创建Popens的列表,然后调用另一个循环的每个元素进行通信。 像这样

processes=[] 
for text in textcollection: 
    myprocess = subprocess.Popen(["myexecutable"], 
       stdin = subprocess.PIPE, stdout = subprocess.PIPE, 
       stderr = None) 
    myprocess.stdin.write(text) 
    processes.append(myprocess) 

for proc in processes: 
    myoutput, err=proc.communicate() 
    #do something with the output here 

这样就不必等到所有的Popens已经开始

+0

不幸的是,这对我来说不起作用,因为它是一个java程序,它在每次运行时都会消耗3G的内存。这就是为什么加载需要很长时间。我无法拥有5000个3G流程的实例。 – JasonMond

+0

我想我明白了。在获得输入文本后,它输出一些内容然后退出?或者是否等待您输入其他内容 –

+0

它输出然后退出。 – JasonMond

5

后,这是communicate()电话是杀害你的子进程。按照subprocess documentationcommunicate()方法:

与互动的过程:将数据发送至标准输入。从stdout和stderr中读取数据,直到达到文件结尾。等待进程终止。

你想要做的是直接与POpen对象的stdinstdout性能直接与子进行交流互动。但是,文档建议对此说法:

警告:使用通信()而不是.stdin.write,启动并阻止子进程。

因此,您需要为潜在的死锁实施您自己的解决方法,或者希望有人为您写了asynchronous subprocess module

编辑:这里有一个如何异步子模块可以使用quick'n'dirty例如:

import asyncsubprocess 

textcollection = ['to', 'be', 'or', 'not', 'to be', 'that is the', 'question'] 

myprocess = asyncsubprocess.Popen(["cat"], 
    stdin = asyncsubprocess.PIPE, 
    stdout = asyncsubprocess.PIPE, 
    stderr = None) 

for text in textcollection: 
    bytes_sent, myoutput, err = myprocess.listen(text) 
    print text, bytes_sent, myoutput, err 

当我运行它,它打印:

to 2 to 
be 2 be 
or 2 or 
not 3 not 
to be 5 to be 
that is the 11 that is the 
question 8 question 
-2
if os.name == 'nt': 
startupinfo = subprocess.STARTUPINFO() 
startupinfo.dwFlags |= subprocess._subprocess.STARTF_USESHOWWINDOW 
subprocess.call(os.popen(tempFileName), shell=True) 
os.remove(tempFileName)