使用subprocess PIPE在两个Python脚本之间发送字符串
我想在我的主Python程序中用subprocess打开一个Python脚本。我希望这两个程序能在运行时互相交流,这样我就可以监控从属脚本的活动,也就是说,我需要它们之间能发送字符串。
主程序会有一个类似下面的函数,用来和从属脚本进行沟通和监控:
脚本 1
import subprocess
import pickle
import sys
import time
import os
def communicate(clock_speed, channel_number, frequency):
p = subprocess.Popen(['C:\\Python27\\pythonw','test.py'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
data = pickle.dumps([clock_speed, channel_number, frequency]).replace("\n", "\\()")
print data
p.stdin.write("Start\n")
print p.stdout.read()
p.stdin.write(data + "\n")
p.poll()
print p.stdout.readline()
print "return:" + p.stdout.readline()
#p.kill()
if __name__ == '__main__':
print "GO"
communicate(clock_speed = 400, channel_number = 0, frequency = 5*1e6)
test.py脚本看起来像这样:
脚本 2
import ctypes
import pickle
import time
import sys
start = raw_input("")
sys.stdout.write("Ready For Data")
data = raw_input("")
data = pickle.loads(data.replace("\\()", "\n"))
sys.stdout.write(str(data))
###BUNCH OF OTHER STUFF###
我希望这些脚本能做到以下几点:
- 脚本 1 用Popen打开脚本 2
- 脚本 1 发送字符串 "Start\n"
- 脚本 2 读取这个字符串并发送字符串 "Ready For Data"
- 脚本 1 读取这个字符串并把序列化的数据发送给脚本 2
- 然后继续其他操作...
主要的问题是如何完成第2到第4步。之后这两个脚本之间的其他通信应该也能顺利进行。目前,我只能在脚本 2结束后读取它的字符串。
任何帮助都非常感谢。
更新:
脚本 1 必须使用32位的Python运行,而脚本 2 必须使用64位的Python运行。
2 个回答
我也遇到过类似的问题,就是在不同的进程之间发送普通的Python对象时,总是会碰到一些麻烦,比如不知道对方是否已经发送了对象,或者对方的进程是否已经关闭。而且,使用 multiprocessing.Queue
通常意味着这个进程必须是由当前进程启动的,但有时候两个进程是想要合作的,这种情况并不总是成立。
为了解决这个问题,我使用了 picklepipe
模块。这个模块定义了一种通用的对象序列化管道接口,还有一种使用 pickle
协议的管道,叫做 PicklePipe
(还有一种使用 marshal
协议的管道,叫做 MarshalPipe
)。它不仅可以发送字符串,还可以发送任何可以被序列化的对象给对方。
这些管道甚至是可选择的,这意味着它们可以被 selectors
模块(或者 selectors2
, selectors34
)当作文件对象使用,当有新对象准备好接收时。这使得等待多个不同管道准备好变得非常高效。
支持Python 2.7及以上版本(可能也支持2.6),并且可以在所有主要平台上使用。甚至可以在两个不同版本的Python之间发送对象!可以查看 项目文档 或者 在Github上查看源代码。
声明:我是 picklepipe
的作者,欢迎大家给我反馈。:)
管道的问题在于,如果你调用读取操作但没有东西可读,你的代码就会停在那里,直到另一方写入一些内容供你读取。而且如果你写得太多,下一次写操作可能会被阻塞,直到另一方从管道中读取一些内容并释放空间。
有一些“非阻塞调用”,在这种情况下会返回错误,而不是让你的代码停住,但你的应用程序仍然需要合理处理这些错误。
无论如何,你需要建立某种协议。想想HTTP或者你熟悉的其他协议:有请求和响应,当你在读取其中一个时,协议总是会告诉你是否还有其他东西需要读取。这样你就可以根据情况决定是否等待更多数据。
这里有一个有效的例子。它之所以有效,是因为有以下协议:
- p1发送一行,以'\n'结尾;
- p2也发送一行;
- p1再发送一行;
- p2也再发送一行;
- 双方都满意,然后退出。
为了向管道(任一方)写入一行并确保它被写入管道,我会先调用write()
,然后调用flush()
。
为了从管道(任一方)读取一行,但不多于一字节,这样会阻塞我的代码直到这一行准备好且不超过这一行,我使用readline()
。
你可以进行其他调用和使用其他协议,包括现成的协议,但单行协议对于简单的事情和像这样的演示效果很好。
p1.py:
import subprocess
p = subprocess.Popen(['python', 'p2.py'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
p.stdin.write("Hello\n")
p.stdin.flush()
print 'got', p.stdout.readline().strip()
p.stdin.write("How are you?\n")
p.stdin.flush()
print 'got', p.stdout.readline().strip()
p2.py:
import sys
data = sys.stdin.readline()
sys.stdout.write("Hm.\n")
sys.stdout.flush()
data = sys.stdin.readline()
sys.stdout.write("Whatever.\n")
sys.stdout.flush()