通过SSH读取远程服务器日志时Python subprocess连接异常断开
问题描述
我尝试通过SSH连接服务器的特定端口并读取日志,但用Python subprocess执行相关命令时,SSH连接在认证完成后立即关闭。我使用的命令为:sshpass -p xxxx ssh -tt -v root@xxx.xxx.xxx.xx -p 2200 | tee -i -a remoteserver.log。调试日志如下:
debug1: Authentication succeeded (password).
debug1: channel 0: new [client-session]
debug1: Entering interactive session.
debug1: pledge: network
debug1: tty_make_modes: no fd or tio
debug1: Sending environment.
debug1: Sending env LANG = en_US
debug1: client_input_channel_req: channel 0 rtype exit-status reply 0
debug1: channel 0: free: client-session, nchannels 1
debug1: fd 0 clearing O_NONBLOCK
debug1: fd 1 clearing O_NONBLOCK
debug1: fd 2 clearing O_NONBLOCK
Connection to xxx.xxx.xxx.x closed.
Transferred: sent 1808, received 1440 bytes, in 0.0 seconds
Bytes per second: sent 36560.7, received 29119.1
debug1: Exit status 0
注:该命令在终端直接执行时运行正常。有人遇到过类似问题吗?
解决办法
这种情况核心是Python subprocess默认的环境和终端交互环境不匹配——终端里有完整的tty支持,能维持SSH的交互式会话,但subprocess默认的流处理会导致SSH在认证后没有输入源,直接退出。试试以下几种方案:
给subprocess模拟终端环境
用Popen时显式指定流参数,同时开启终端模拟:import subprocess cmd = "sshpass -p xxxx ssh -tt -v root@xxx.xxx.xxx.xx -p 2200 | tee -i -a remoteserver.log" # 开启shell支持管道,绑定标准流并模拟新会话 proc = subprocess.Popen( cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, start_new_session=True ) # 保持进程运行,读取输出 output, _ = proc.communicate() print(output.decode())把tee逻辑移到Python里,去掉管道依赖
直接捕获SSH输出并写入日志文件,避免管道带来的流同步问题:import subprocess ssh_cmd = "sshpass -p xxxx ssh -tt -v root@xxx.xxx.xxx.xx -p 2200" proc = subprocess.Popen( ssh_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True ) # 实时写入日志并打印 with open("remoteserver.log", "a") as log_file: for line in proc.stdout: print(line.strip()) log_file.write(line)用pexpect处理交互式会话
如果上述方法还是不行,试试专门处理终端交互的pexpect库,它能更好地模拟终端环境:import pexpect ssh_cmd = "sshpass -p xxxx ssh -tt -v root@xxx.xxx.xxx.xx -p 2200" child = pexpect.spawn(ssh_cmd) with open("remoteserver.log", "a") as log_file: while True: try: line = child.readline() if not line: break content = line.decode() print(content.strip()) log_file.write(content) except pexpect.EOF: break
内容的提问来源于stack exchange,提问作者mrutyunjay

