Pexpect无法获取标记后内容,求完善run_command_and_return_status_and_output函数
问题描述
使用pexpect获取SSH会话中执行命令的完整输出时,尝试多种方法后仍只能获取标记前的内容,无法获取标记后的内容,请求完善run_command_and_return_status_and_output函数。
原代码
import pexpect import sys def ssh_login(ssh_host, ssh_username, ssh_password, timeout=10): ssh_command = f"ssh {ssh_username}@{ssh_host}" child = pexpect.spawn(ssh_command, timeout=timeout) try: # Expect the SSH password prompt index = child.expect([pexpect.TIMEOUT, 'password:', pexpect.EOF], timeout=timeout) if index == 0: raise Exception("SSH connection timeout") if index == 1: # Send the SSH password child.sendline(ssh_password) # Expect the shell prompt after successful login index = child.expect(['7503', pexpect.EOF], timeout=timeout) if index == 0: return child # Successfully logged in raise Exception("Authentication failed") except Exception as e: child.close() raise e def run_command_and_return_status_and_output(child, command, success_marker, failure_marker, timeout=10): status = "Pass" # Initialize status as Pass, assuming the command will pass output = "" try: # Send the command child.sendline(command) # Expect the output index = child.expect([success_marker, failure_marker, pexpect.EOF, pexpect.TIMEOUT], timeout=timeout) if index == 0: status = "Pass" if index == 1: status = "Fail" if index not in [0, 1]: status = "Exception" output = child.before.decode('utf-8')+child.after.decode('utf-8') if output: # Remove the command from the output output = output.replace(command, "") except Exception as e: status = "Exception" output = str(e) finally: return status, output def run_command_and_return_status_and_output(child, command, success_marker, failure_marker, timeout=10): status = "Pass" # Initialize status as Pass, assuming the command will pass output = "" try: # Send the command child.sendline(command) # Expect the output index = child.expect([success_marker, failure_marker, pexpect.EOF, pexpect.TIMEOUT], timeout=timeout) if index == 0: status = "Pass" elif index == 1: status = "Fail" # Get the entire output, including before and after output = child.before.decode('utf-8') + child.after.decode('utf-8') except Exception as e: status = "Exception" output = str(e) finally: return status, output # Example usage ssh_host = "your host" ssh_username = "your username" ssh_password = "your password" child = ssh_login(ssh_host, ssh_username, ssh_password) command = "ifconfig -a" success_marker = "lo" failure_marker = "xxx" timeout = 10 status, output = run_command_and_return_status_and_output(child, command, success_marker, failure_marker, timeout) print("Status:", status) print("Output:", output) # Close the SSH connection child.close()
问题分析
原函数的核心问题是只匹配到成功/失败标记就停止读取输出,但命令执行完成后,输出内容会一直延续到shell提示符出现,这部分后续内容并没有被捕获。比如执行ifconfig -a时,匹配到"lo"(success_marker)就停止,后续的网卡信息和最终的shell提示符"7503"都没被读取。
解决方案
修改函数逻辑:先等待命令执行完成(即匹配到shell提示符),同时在等待过程中监控成功/失败标记;待获取完整输出后,再根据标记判断执行状态。
修改后的完整代码如下:
import pexpect import sys def ssh_login(ssh_host, ssh_username, ssh_password, timeout=10): ssh_command = f"ssh {ssh_username}@{ssh_host}" child = pexpect.spawn(ssh_command, timeout=timeout) try: index = child.expect([pexpect.TIMEOUT, 'password:', pexpect.EOF], timeout=timeout) if index == 0: raise Exception("SSH connection timeout") if index == 1: child.sendline(ssh_password) # 保存shell提示符,后续执行命令时需要用到 prompt = '7503' index = child.expect([prompt, pexpect.EOF], timeout=timeout) if index == 0: return child, prompt # 返回child和提示符 raise Exception("Authentication failed") except Exception as e: child.close() raise e def run_command_and_return_status_and_output(child, command, success_marker, failure_marker, prompt, timeout=10): status = "Exception" output = "" try: # 发送命令 child.sendline(command) # 同时监控:成功标记、失败标记、shell提示符、超时、EOF # 优先级:先匹配成功/失败标记,再匹配提示符 index = child.expect([success_marker, failure_marker, prompt, pexpect.TIMEOUT, pexpect.EOF], timeout=timeout) # 获取完整输出:从发送命令后到匹配到目标内容的所有输出 full_output = child.before.decode('utf-8') + child.after.decode('utf-8') # 去除命令本身和末尾的提示符,清理输出格式 output = full_output.replace(f"{command}\r\n", "").replace(prompt, "").strip() # 根据匹配结果判断状态 if index == 0: status = "Pass" elif index == 1: status = "Fail" elif index == 2: # 如果先匹配到提示符,检查输出中是否包含成功/失败标记 if success_marker in full_output: status = "Pass" elif failure_marker in full_output: status = "Fail" else: status = "Unknown" else: status = "Exception" except Exception as e: status = "Exception" output = str(e) finally: return status, output # Example usage ssh_host = "your host" ssh_username = "your username" ssh_password = "your password" # 登录时获取child和shell提示符 child, prompt = ssh_login(ssh_host, ssh_username, ssh_password) command = "ifconfig -a" success_marker = "lo" failure_marker = "xxx" timeout = 10 status, output = run_command_and_return_status_and_output(child, command, success_marker, failure_marker, prompt, timeout) print("Status:", status) print("Output:", output) # Close the SSH connection child.close()
关键改动说明
- ssh_login函数:新增返回shell提示符
prompt,后续执行命令时需要等待这个提示符来确认命令执行完成。 - run_command_and_return_status_and_output函数:
- 调整expect的匹配列表,加入shell提示符,确保能捕获命令执行的全部输出。
- 先获取完整输出,再去除命令本身和末尾的提示符,得到干净的结果。
- 增加逻辑:如果先匹配到提示符,再在完整输出中检查成功/失败标记,避免因标记出现在输出中间导致的截断。
内容的提问来源于stack exchange,提问作者user1334609
相关产品推荐
相关产品推荐

