使用subprocess PIPE在两个Python脚本之间发送字符串

3 投票
2 回答
7273 浏览
提问于 2025-04-17 19:00

我想在我的主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. 脚本 1 用Popen打开脚本 2
  2. 脚本 1 发送字符串 "Start\n"
  3. 脚本 2 读取这个字符串并发送字符串 "Ready For Data"
  4. 脚本 1 读取这个字符串并把序列化的数据发送给脚本 2
  5. 然后继续其他操作...

主要的问题是如何完成第2到第4步。之后这两个脚本之间的其他通信应该也能顺利进行。目前,我只能在脚本 2结束后读取它的字符串。

任何帮助都非常感谢。

更新:

脚本 1 必须使用32位的Python运行,而脚本 2 必须使用64位的Python运行。

2 个回答

0

我也遇到过类似的问题,就是在不同的进程之间发送普通的Python对象时,总是会碰到一些麻烦,比如不知道对方是否已经发送了对象,或者对方的进程是否已经关闭。而且,使用 multiprocessing.Queue 通常意味着这个进程必须是由当前进程启动的,但有时候两个进程是想要合作的,这种情况并不总是成立。

为了解决这个问题,我使用了 picklepipe 模块。这个模块定义了一种通用的对象序列化管道接口,还有一种使用 pickle 协议的管道,叫做 PicklePipe(还有一种使用 marshal 协议的管道,叫做 MarshalPipe)。它不仅可以发送字符串,还可以发送任何可以被序列化的对象给对方。

这些管道甚至是可选择的,这意味着它们可以被 selectors 模块(或者 selectors2, selectors34)当作文件对象使用,当有新对象准备好接收时。这使得等待多个不同管道准备好变得非常高效。

支持Python 2.7及以上版本(可能也支持2.6),并且可以在所有主要平台上使用。甚至可以在两个不同版本的Python之间发送对象!可以查看 项目文档 或者 在Github上查看源代码

声明:我是 picklepipe 的作者,欢迎大家给我反馈。:)

4

管道的问题在于,如果你调用读取操作但没有东西可读,你的代码就会停在那里,直到另一方写入一些内容供你读取。而且如果你写得太多,下一次写操作可能会被阻塞,直到另一方从管道中读取一些内容并释放空间。

有一些“非阻塞调用”,在这种情况下会返回错误,而不是让你的代码停住,但你的应用程序仍然需要合理处理这些错误。

无论如何,你需要建立某种协议。想想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()

撰写回答