如何使用Popen交互式获取子进程输出并向子进程发送输入?
问题根源
你的代码卡住核心有3个原因:
- 子进程缓冲机制问题:OVFTool这类工具检测到标准输出是管道而非交互式终端时,会默认开启块缓冲而非行缓冲,输出内容攒到一定大小才会刷出,你即使flush自己的stdin也读不到子进程还没刷出的提示内容,
proc.stdout.flush()是无效操作,父进程无法主动刷新子进程的输出缓冲区。 - 换行符适配错误:Windows系统下命令行工具识别的输入结束符是
\r\n而非\n,你写入用户名密码时只加了\n,OVFTool会认为你还没完成输入,自然不会输出后续内容。 - 密码输入场景的特殊处理:绝大多数CLI工具在密码输入阶段不会输出换行符,
readline()会一直等待换行符出现才返回,你后续用固定长度读也会因为子进程没刷缓冲而阻塞。
修复方案
1. 基础修改就能解决大部分情况
调整输入换行符、关闭不必要的参数、改用参数列表传参避免转义问题:
import subprocess import os from time import sleep def export_extract_ova_file(): vm_name = input("What is the name of the vm you are exporting?") print(f"Exporting {vm_name} from VSphere.") # 改用参数列表传参,避免shell=True和引号转义问题 cmd = [ ovf_tool_path, f"vi://example_ip/{vm_name}", f"./{vm_name}.ova" ] with subprocess.Popen(cmd, text=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=0) as proc: # 读用户名提示 proc_read = proc.stdout.readline() print(proc_read) username = input("Please enter your username") # 用os.linesep自动适配系统换行符 proc.stdin.write(username + os.linesep) proc.stdin.flush() # 读密码提示 proc_read = proc.stdout.readline() print(proc_read) password = input("Please enter your password") proc.stdin.write(password + os.linesep) proc.stdin.flush() # 循环逐字符读后续输出,不会阻塞 while True: char = proc.stdout.read(1) if not char: # 子进程已退出 break print(char, end='')
2. 极端情况适配
如果上面修改还是卡住,说明OVFTool必须要交互式TTY环境才能正常输出,Windows下可以用winpty封装调用,或者直接用模拟终端的第三方库实现交互,就能完全匹配手动执行时的输出逻辑。
内容的提问来源于stack exchange,提问作者jollygreen
相关产品推荐
相关产品推荐

