SONiC设备使用pexpect.spawn运行bash脚本触发EOF异常排查
问题概述
在SONiC设备环境中,使用Python pexpect模块的spawn方法调用bash运行/home/admin/eeprom_test_01.sh测试脚本,计划通过expect匹配交互提示、sendline自动输入应答完成自动化测试,初始实现代码如下:
import pexpect child = pexpect.spawn("/bin/bash", ['/home/admin/eeprom_test_01.sh']) child.expect("Type 'Continue' to proceed, else abort:") child.sendline("Continue") child.expect("Select Capitaine revision\(EVT or DVT\):") child.sendline("DVT") child.expect("EEPROM Device: COMe") return child
运行代码时触发pexpect.exceptions.EOF异常,自动化流程中断,关键报错信息如下:
EOF: End Of File (EOF). Exception style platform. <pexpect.pty_spawn.spawn object at 0x7fa381be9978> command: /bin/bash args: ['/bin/bash', '/home/admin/eeprom_test_01.sh'] before (last 100 chars): b'/bin/bash: /home/admin/eeprom_test_01.sh: No such file or directory\r\n' exitstatus: 127 flag_eof: True searcher: searcher_re: 0: re.compile(b"Type 'Continue' to proceed, else abort:")
从报错信息可以确认:bash提示目标脚本路径不存在,子进程退出状态码为127,在匹配首个交互提示前子进程就已终止,触发EOF异常。
根因定位
报错核心指向执行代码的运行上下文中,无法通过代码里写的绝对路径找到目标测试脚本,常见触发场景:
- 脚本实际存放路径和代码中填写的路径不一致,存在文件名拼写错误、大小写错误、文件被移动/删除的情况
- 运行Python代码的用户权限不足,无法访问
/home/admin目录或目标脚本文件 - SONiC系统大部分功能运行在隔离容器中,如果这段Python代码是在容器内执行,容器默认不会挂载宿主机的
/home/admin目录,会导致容器内的bash找不到对应脚本
注:当前spawn的传参逻辑没有问题,报错中打印的执行参数为['/bin/bash', '/home/admin/eeprom_test_01.sh'],对应执行的命令就是/bin/bash /home/admin/eeprom_test_01.sh,符合预期;且显式调用bash执行脚本不需要脚本本身具备可执行权限,仅需要读权限,权限问题仅会触发Permission denied报错,和当前No such file or directory的报错特征不符。
修复方案
按以下优先级排查修复:
- 先进入和Python代码完全一致的运行上下文(同用户、同容器/宿主机环境),手动执行命令验证文件可达性:
/bin/bash /home/admin/eeprom_test_01.sh- 如果手动执行同样报No such file or directory:
- 容器内运行场景:将测试脚本拷贝到容器内的
/home/admin路径下,或启动容器时增加挂载参数,把宿主机/home/admin目录映射到容器内相同路径 - 宿主机运行场景:执行
find / -name "eeprom_test_01.sh"定位脚本真实存放路径,将代码中的脚本路径替换为查到的实际绝对路径
- 容器内运行场景:将测试脚本拷贝到容器内的
- 如果手动执行同样报No such file or directory:
- 路径问题修复后,优化代码鲁棒性,避免异常直接中断流程,优化后参考代码:
import pexpect import sys def run_eeprom_test(): try: # 直接传完整命令字符串写法更简洁不易出错,增加编码、超时配置,开启读日志方便调试 child = pexpect.spawn( "/bin/bash /home/admin/eeprom_test_01.sh", encoding="utf-8", timeout=60 ) child.logfile_read = sys.stdout # 每一步expect都把EOF、TIMEOUT加入匹配列表,提前捕获异常 match_res = child.expect([ "Type 'Continue' to proceed, else abort:", pexpect.EOF, pexpect.TIMEOUT ]) if match_res != 0: print(f"脚本启动失败,缓冲区输出:{child.before}") child.close() return None child.sendline("Continue") match_res = child.expect([ "Select Capitaine revision\(EVT or DVT\):", pexpect.EOF, pexpect.TIMEOUT ]) if match_res != 0: print(f"确认步骤执行失败,缓冲区输出:{child.before}") child.close() return None child.sendline("DVT") match_res = child.expect([ "EEPROM Device: COMe", pexpect.EOF, pexpect.TIMEOUT ]) if match_res != 0: print(f"版本选择步骤执行失败,缓冲区输出:{child.before}") child.close() return None return child except Exception as e: print(f"测试执行异常:{str(e)}") return None
内容的提问来源于stack exchange,提问作者Preet
相关产品推荐
相关产品推荐

