如何持续向subprocess子进程的stdin写入数据并保持原生终端功能
问题分析
你之前的两个方案都存在本质缺陷,无法实现实时交互效果:
- main1.py的问题:
subprocess.run()是阻塞执行直到子进程退出,你传入input="foo\n"相当于把这串内容一次性写入子进程的stdin后直接关闭stdin,子进程里的输入读取逻辑读到EOF后直接抛出异常,进程就退出了,所以看起来是被立刻杀死。 - main2.py的问题:
subprocess.call()同样是阻塞调用,会一直等子进程运行结束才返回,主进程在等待期间完全没法和子进程交互,自然没法持续传入数据。
核心解决思路
要实现和终端原生运行一致的交互效果,需要满足两个核心条件:
- 不阻塞主进程,同时可以实时给子进程的stdin写入输入
- 实时读取子进程的stdout/stderr输出,显示到当前终端
另外必须解决Python默认的输出缓冲问题:子进程输出到管道时默认会启用块缓冲,输出内容不会立刻打印,需要加-u参数启动Python禁用缓冲,或者在所有print语句后加flush操作。
修正子脚本(适配Python 2.7)
你贴的subscript.py存在多处不兼容Python2.7以及语法错误,先修正为可用版本:
import threading from Queue import Queue # Python2的队列模块首字母大写 import argparse class FooBar(): def __init__(self): self.q = Queue() foo = threading.Thread(target=self.foo) bar = threading.Thread(target=self.bar) # 设为守护线程,主进程退出时自动结束 foo.daemon = True bar.daemon = True foo.start() bar.start() # 缩进修正,作为类方法存在 def foo(self): while True: text = raw_input() # Python2用raw_input获取标准输入 print("%s is sent by foo()" % text) # Python2不支持f-string,用%格式化 self.q.put(text) def bar(self): while True: if not self.q.empty(): text = self.q.get_nowait() print("%s is sent by bar()" % text) if __name__ == "__main__": c = FooBar() # 加阻塞防止主进程直接退出 while True: pass
完整主进程实现
import subprocess import threading import sys def read_subprocess_output(proc): # 实时读取子进程输出并打印到当前终端 while True: line = proc.stdout.readline() if not line: break sys.stdout.write(line) sys.stdout.flush() if __name__ == "__main__": # 加-u参数禁用Python输出缓冲 proc = subprocess.Popen( ["python", "-u", "subscript.py"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, # 把错误输出合并到标准输出统一处理 text=True ) # 启动读输出的守护线程 output_thread = threading.Thread(target=read_subprocess_output, args=(proc,)) output_thread.daemon = True output_thread.start() # 主进程读取用户输入,写入子进程stdin try: while True: # 主脚本如果用Python2运行,把这里的input改为raw_input input_text = input() + "\n" proc.stdin.write(input_text) proc.stdin.flush() except (KeyboardInterrupt, EOFError): # 处理Ctrl+C退出逻辑 proc.terminate() proc.wait()
运行效果说明
现在运行主脚本,就可以和直接终端运行subscript.py的效果完全一致:输入任意内容回车,就能看到foo和bar的打印输出,支持持续输入,按Ctrl+C可以正常退出。
内容的提问来源于stack exchange,提问作者Reactionic
相关产品推荐
相关产品推荐

