如何用subprocess.Popen保持会话打开并读取管道输出以传递参数
问题解决:保持subprocess管道打开时读取命令输出
你的核心问题是:bash会话未退出时,stdout不会触发EOF,readline()会一直阻塞等待输入,永远不会返回空字符串,导致循环无法终止。Ctrl+C只是强制中断了循环,此时command1的输出已经全部被读取,所以output_lines能被正确填充。
为什么原代码失效?
用subprocess.Popen启动bash后,这个进程会持续存活(因为要保留会话执行后续的command2),它的stdout管道不会关闭。只有当进程退出时,readline()才会读到空字符串触发终止条件。而command1执行完毕后,bash只是回到等待输入的状态,并未退出,所以你的循环会一直卡在readline()上。
可行解决方案
方案1:给command1添加结束标记(最可靠)
让command1执行完后输出一个唯一的结束标识,循环检测到这个标识就停止读取,而非等待空行。
import subprocess bash_session = subprocess.Popen(['bash'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # 执行command1并追加结束标记 bash_session.stdin.write(b"command1; echo '--COMMAND1-FINISHED--'\n") bash_session.stdin.flush() output_lines = [] while True: line = bash_session.stdout.readline().decode().strip() # 检测到结束标记就终止循环 if line == '--COMMAND1-FINISHED--': break if line: # 跳过空行 output_lines.append(line) # 用command1的输出执行command2 if output_lines: last_output = output_lines[-1] bash_session.stdin.write(f"command2 {last_output}\n".encode()) bash_session.stdin.flush() # 如需读取command2的输出,可复用结束标记逻辑
方案2:用select设置读取超时(适合无法修改command1的场景)
用select模块监听stdout,设置超时时间,超过时间无新输出则判定command1执行完毕:
import subprocess import select bash_session = subprocess.Popen(['bash'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) bash_session.stdin.write(b"command1\n") bash_session.stdin.flush() output_lines = [] while True: # 监听stdout,超时设为5秒(可根据实际调整) ready, _, _ = select.select([bash_session.stdout], [], [], 5) if not ready: # 超时,判定command1执行完成 break line = bash_session.stdout.readline().decode().strip() if line: output_lines.append(line) # 执行command2 if output_lines: last_output = output_lines[-1] bash_session.stdin.write(f"command2 {last_output}\n".encode()) bash_session.stdin.flush()
注意事项
- 方案1的结束标记要选用不会出现在command1正常输出中的字符串,避免误判。
- 方案2的超时时间需根据command1的实际执行时长调整,过短可能漏读,过长会浪费时间。
- 全程不要关闭bash的stdin/stdout,否则后续无法执行command2。
内容的提问来源于stack exchange,提问作者Fabri Ba
相关产品推荐
相关产品推荐

