如何在后台运行pexpect子进程并动态捕获日志?
我来帮你搞定这个pexpect的问题!你现在遇到的核心问题是child.read()是阻塞式调用,会一直等子进程跑完才往下走,没法同时让多个子进程后台跑还实时抓取输入输出日志。下面给你几个实用的解决方案:
方法1:用非阻塞读取实现实时监控
这个方法通过read_nonblocking配合短超时,避免主进程被单个子进程阻塞,同时轮询所有后台进程的输出:
import pexpect import time def spawn_background_cmd(cmd): # 创建子进程,设置timeout=None避免超时终止进程 return pexpect.spawn(cmd, timeout=None) # 批量启动子进程 active_processes = [] for idx in range(3): # 替换成你实际要运行的命令 target_cmd = f"ping -c 6 google.com" child_proc = spawn_background_cmd(target_cmd) active_processes.append(child_proc) print(f"已启动进程 {idx+1},PID: {child_proc.pid}") # 实时监控所有进程的输出 while active_processes: # 用list()复制列表,避免遍历中修改原列表导致的异常 for proc in list(active_processes): try: # 非阻塞读取,最多读1024字节,超时0.1秒 raw_output = proc.read_nonblocking(size=1024, timeout=0.1) if raw_output: print(f"[PID {proc.pid}] 输出: {raw_output.decode().strip()}") except pexpect.TIMEOUT: # 超时说明当前没有新输出,跳过继续检查其他进程 pass except pexpect.EOF: # 进程结束,从活跃列表移除 print(f"[PID {proc.pid}] 进程已结束") active_processes.remove(proc) # 短暂休眠,降低CPU占用 time.sleep(0.1)
为什么这个方法管用?
read_nonblocking不会一直卡在那里等输出,一旦超过设置的超时时间就会抛出TIMEOUT异常,我们可以忽略这个异常继续检查其他进程;当进程结束时会抛出EOF,这时就可以把它从监控列表里移除。
方法2:用多线程单独监控每个进程
如果不想写轮询逻辑,可以给每个子进程分配一个单独的线程,专门处理它的输入输出,主进程可以继续创建新进程或者做其他工作:
import pexpect import threading def monitor_single_process(child_proc): try: while True: # 监听超时或进程结束信号 match_idx = child_proc.expect([pexpect.TIMEOUT, pexpect.EOF], timeout=0.5) if match_idx == 0: # 捕获到超时,说明有未读取的输出 if child_proc.before: print(f"[PID {child_proc.pid}] 输出: {child_proc.before.decode().strip()}") elif match_idx == 1: # 进程结束,打印剩余输出 if child_proc.before: print(f"[PID {child_proc.pid}] 最终输出: {child_proc.before.decode().strip()}") print(f"[PID {child_proc.pid}] 进程已结束") break except Exception as e: print(f"[PID {child_proc.pid}] 监控出错: {str(e)}") # 循环创建子进程并启动监控线程 for idx in range(3): target_cmd = f"ping -c 6 google.com" child_proc = pexpect.spawn(target_cmd, timeout=None) print(f"已启动进程 {idx+1},PID: {child_proc.pid}") # 启动守护线程,主进程退出时线程自动结束 monitor_thread = threading.Thread(target=monitor_single_process, args=(child_proc,), daemon=True) monitor_thread.start() # 主进程可以继续执行其他任务,这里用输入模拟等待 input("按回车退出程序...\n")
这个方法的优势
每个进程的日志处理独立,不用在主循环里轮询,代码逻辑更清晰;如果需要给子进程发送输入(比如交互式命令),直接在监控线程里调用child_proc.sendline("你的输入内容")就行,不用依赖interact()。
方法3:用logfile直接重定向输出
如果你的需求只是实时看到输出或者把输出保存到文件,这个方法最省事:
import pexpect import sys active_processes = [] for idx in range(3): target_cmd = f"ping -c 6 google.com" child_proc = pexpect.spawn(target_cmd, timeout=None) # 将子进程的字节流输出重定向到stdout(控制台) child_proc.logfile = sys.stdout.buffer active_processes.append(child_proc) print(f"已启动进程 {idx+1},PID: {child_proc.pid}") # 等待所有进程结束 for proc in active_processes: proc.wait() print(f"[PID {proc.pid}] 进程已结束")
注意点
这里要用sys.stdout.buffer,因为pexpect输出的是字节流,直接用sys.stdout会报错;如果要保存到文件,把sys.stdout.buffer换成打开的文件对象就行(比如open("output.log", "wb"))。
内容的提问来源于stack exchange,提问作者dheeraj tripathi
相关产品推荐
相关产品推荐

