You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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()
关键改动说明
  1. ssh_login函数:新增返回shell提示符prompt,后续执行命令时需要等待这个提示符来确认命令执行完成。
  2. run_command_and_return_status_and_output函数:
    • 调整expect的匹配列表,加入shell提示符,确保能捕获命令执行的全部输出。
    • 先获取完整输出,再去除命令本身和末尾的提示符,得到干净的结果。
    • 增加逻辑:如果先匹配到提示符,再在完整输出中检查成功/失败标记,避免因标记出现在输出中间导致的截断。

内容的提问来源于stack exchange,提问作者user1334609

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 03:05:12