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

如何通过Python logging记录Paramiko exec_command的stdin/stdout/stderr?

解决Paramiko exec_command日志记录时的"File not open for reading"错误

我刚帮你排查了这个问题,核心原因其实很简单:你代码里对stdin调用了readlines(),但Paramiko的stdin是只写流,根本不能用来读取内容,这直接触发了那个IO错误。

咱们一步步来解决:

错误原因拆解

Paramiko通过exec_command返回的三个对象里:

  • stdin:是ChannelStdinFile类型,专门用来给远程命令输入数据(比如输入密码、交互指令),它的打开模式是只写,所以任何读取操作(比如read()、readlines())都会抛出"File not open for reading"错误。
  • stdout和stderr:才是可读的输出流,用来获取远程命令的正常输出和错误信息。

修复后的代码

我给你调整了client_exe函数,修正了stdin的处理,同时优化了输出读取的逻辑(避免缓冲区死锁,确保读取完整):

def client_exe(hostname, command, username, password):
    logger = getLogger("EXEC", "log.log")
    ssh_client = paramiko.SSHClient()
    ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    try:
        ssh_client.connect(hostname=hostname, username=username, password=password)
        stdin, stdout, stderr = ssh_client.exec_command(command, get_pty=True)
        
        # stdin是写入流,无法读取,这里标注说明
        stdin_info = "Write-only stream (no input sent to remote command)"
        
        # 先获取命令退出状态,确保所有输出都已写入缓冲区
        exit_status = stdout.channel.recv_exit_status()
        # 读取完整的输出和错误信息
        stdout_content = stdout.readlines()
        stderr_content = stderr.readlines()
        
        logger.info(
            "Remote Machine: {} \n"
            "\tCommand: {} \n"
            "\tSTDIN: {} \n"
            "\tSTDOUT: {} \n"
            "\tErrors: {} \n"
            "\tExit Status: {}".format(
                hostname, command, stdin_info, stdout_content, stderr_content, exit_status
            )
        )
    finally:
        # 确保SSH客户端始终关闭,避免资源泄漏
        ssh_client.close()

额外优化说明

  1. stdin的写入场景:如果你的业务需要给远程命令输入内容(比如执行sudo时输入密码),可以这样操作stdin,但依然不能读取它:
# 示例:给远程命令输入密码
stdin.write("your_sudo_password\n")
stdin.flush()  # 确保数据发送到远程端
  1. 避免日志重复输出:你的getLogger函数每次调用都会重复添加Handler,会导致同一条日志多次输出,我给你加个判断优化一下:
def getLogger(name, logname):
    logger = logging.getLogger(name)
    # 避免重复添加Handler
    if logger.handlers:
        return logger
    fmt = logging.Formatter("%(asctime)s %(levelname)s: %(message)s", datefmt='%m/%d/%Y %I:%M:%S %p')
    fileHandler = logging.FileHandler(logname, mode="a")
    fileHandler.setFormatter(fmt)
    streamHandler = logging.StreamHandler()
    streamHandler.setFormatter(fmt)
    logger.setLevel(logging.DEBUG)
    logger.addHandler(fileHandler)
    logger.addHandler(streamHandler)
    return logger

这样修改后,你的日志记录就能正常工作,不会再触发那个IO错误了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 08:52:11