通过 stdin/stdout 与另一个正在运行的进程交互
为了模拟 Python 脚本启动命令行交互式进程并通过 stdin/stdout 发送/接收文本的用例,我们有一个主脚本,它启动另一个运行简单交互式循环的 Python 进程。
这也适用于 Python 脚本需要启动另一个进程并在输入时读取其输出而没有任何交互性的情况。
主脚本
import subprocess
import threading
import queue
import time
if __name__ == '__main__':
def enqueue_output(outp, q):
for line in iter(outp.readline, ''):
q.put(line)
outp.close()
q = queue.Queue()
p = subprocess.Popen(["/usr/bin/python", "/test/interact.py"],
stdin = subprocess.PIPE,
stdout = subprocess.PIPE,
# stderr = subprocess.STDOUT,
bufsize = 1,
encoding ='utf-8')
th = threading.Thread(target=enqueue_output, args=(p.stdout, q))
th.daemon = True
th.start()
for i in range(4):
print("dir()", file=p.stdin)
print(f"Iteration ({i}) Parent received: {q.get()}", end='')
# p.stdin.write("dir()\n")
# while q.empty():
# time.sleep(0)
# print(f"Parent: {q.get_nowait()}")
interact.py 脚本
if __name__ == '__main__':
for i in range(2):
cmd = raw_input()
print("Iteration (%i) cmd=%s" % (i, cmd))
result = eval(cmd)
print("Iteration (%i) result=%s" % (i, str(result)))
输出
Iteration (0) Parent received: Iteration (0) cmd=dir()
Iteration (1) Parent received: Iteration (0) result=['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'cmd', 'i']
Iteration (2) Parent received: Iteration (1) cmd=dir()
Iteration (3) Parent received: Iteration (1) result=['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'cmd', 'i', 'result']
此问答用于模拟来自目标进程的非阻塞读取:https://stackoverflow.com/a/4896288/7915759
该方法提供了一种在主线程中不阻塞地检查输出的方法; q.empty() 会告诉你是否没有数据。您也可以使用q.get() 或超时q.get(2) 来处理阻塞呼叫 - 参数是秒数。它可以是小于零的浮点值。
进程之间基于文本的交互可以在没有线程和队列的情况下完成,但是这个实现提供了更多关于如何检索返回的数据的选项。
Popen() 参数,bufsize=1 和 encoding='utf-8' 可以使用主脚本中的<stdout>.readline(),并将编码设置为两个进程都可以理解的 ascii 兼容编解码器(1 不是缓冲区的大小,这是一个表示行缓冲的符号值)。
使用此配置,两个进程可以简单地使用print() 相互发送文本。此配置应该兼容许多基于交互式文本的命令行工具。