paramiko python 模块挂在 stdout.read()

问题描述 投票:0回答:7

我正在使用以下代码:

import paramiko

def runSshCmd(hostname, username, password, cmd, timeout=None):          
    client = paramiko.SSHClient()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    client.connect(hostname, username=username, password=password,
            allow_agent=False, look_for_keys=False, timeout=timeout) 
    stdin, stdout, stderr = client.exec_command(cmd)
    stdin.flush()
    data = stdout.read()
    print (data)
    client.close()

runSshCmd("10.128.12.32", "root", "C0mput3Gr!d", "ts_menu")

当涉及到 stdout.read() 时,它会挂起......有时它会在很长时间后打印输出。

您能否建议是否可以解决此问题?

我看到这个问题已在以下位置报告:

https://bugs.python.org/issue24026

python中有没有更好的模块用于ssh连接和运行命令??

python python-3.x paramiko python-module
7个回答
20
投票

可能与https://github.com/paramiko/paramiko/issues/109

有关

以下是我所面临的问题以及我如何解决它的解释。

我也遇到过这个问题,原因是 stdout.channel.eof_received == 0

import paramiko
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect("1.1.1.1", username="root", password="pass")
stdin, stdout, stderr = client.exec_command("service XXX start")

stdin、stdout 和 stderr 保持开放状态...

>>> print stdin
<paramiko.ChannelFile from <paramiko.Channel 3 (open) window=2097152 in-buffer=50 -> <paramiko.Transport at 0x17eff90L (cipher aes128-ctr, 128 bits) (active; 1 open channel(s))>>>
>>> print stdout
<paramiko.ChannelFile from <paramiko.Channel 3 (open) window=2097152 in-buffer=50 -> <paramiko.Transport at 0x17eff90L (cipher aes128-ctr, 128 bits) (active; 1 open channel(s))>>>
>>> print stderr
<paramiko.ChannelFile from <paramiko.Channel 3 (open) window=2097152 in-buffer=50 -> <paramiko.Transport at 0x17eff90L (cipher aes128-ctr, 128 bits) (active; 1 open channel(s))>>>

所以没有收到EOF...

>>> print stdin.channel.eof_received
0

通常我收到 True 并且只能 stdout.read(),但为了安全起见,我使用此解决方法(有效!): 等待超时,强制stdout.channel.close(),然后stdout.read():

>>> timeout = 30
>>> import time
>>> endtime = time.time() + timeout
>>> while not stdout.channel.eof_received:
...     sleep(1)
...     if time.time() > endtime:
...         stdout.channel.close()
...         break
>>> stdout.read()
'Starting XXX: \n[  OK  ]\rProgram started . . .\n'
>>>

顺便说一句,我使用:

Python 2.6.6
paramiko (1.15.2)

希望这有帮助...


6
投票

我碰巧遇到了这个问题。但我通过使用“readline”而不是“readlines”来解决这个问题。

例如:

client = paramiko.SSHClient()
client.connect(addr, port, username, password)
stdin, stdout, stderr = client.exec_command(cmd)

while True:
    print(stdout.readline())
    if stdout.channel.exit_status_ready():
        break

因此它将立即打印每一行并且不再挂起,同时 exit_status_ready() 将确保当 stdout 停止/退出时循环中断。


3
投票

当 stdout 中没有数据或有一行没有 eol 时(即在 sh 脚本内的 read 语句中),就会发生这种情况。尝试设置“get_pty=True”,然后仅读取标准输出中的字节。为了避免无限循环,尽管有 continue 语句,但设置超时和睡眠是个好主意:

stdin, stdout, stderr = ssh.exec_command("your-command",get_pty=True)
stdout.flush()
nbytes = 0
while (len(stdout.channel.in_buffer)==0):
     continue

nbytes=len(stdout.channel.in_buffer)
print(nbytes)
stdout.read(nbytes)

2
投票

在使用 stdout 之前尝试关闭 stdin。

环境:
python3.8
paramiko-2.7.2

stdin, stdout, stderr = client.exec_command(cmd)
stdin.close()
for line in iter(stout.readline,""):
    print(line, end='')

0
投票

获取通道时,单独设置一个pty,以便命令结束时可以立即获取EOF。否则,不确定系统中的其他进程是否会影响stdout。

client.connect(hostname='127.0.0.1', port=2222, username='root', password='xxx',
               timeout=10, banner_timeout=300)
cmd = "echo $PATH\n"
stdin, stdout, stderr = client.exec_command(cmd, get_pty=True)
stdout.readlines()

client.connect(hostname='127.0.0.1', port=2222, username='root', password='xxx',
               timeout=10, banner_timeout=300)
channel = client.get_transport().open_session(timeout=10)
channel.get_pty()
channel.settimeout(10)
cmd = f'echo $PATH\n '
channel.exec_command(cmd)
stdin = channel.makefile('wb')
stdout = channel.makefile('r')
stdout.readlines()

-1
投票

对于仍在为此苦苦挣扎的人:在尝试从

client.close()
stdout
读取之前,我可以通过运行
stderr
来解决此问题。


-1
投票

如果您希望在命令完成后得到特定输出,您可以删除 data = stdout.read() 并编写此内容

        while True:
            if "Ended" in str(stdout.readline()):
                stdout.channel.close()
                break

在我的情况下,我的命令将返回“已结束”的内容,因此我会检查它,如果是,则执行 stdout.channel.close() 这解决了我的问题。在您的情况下,“结束”可以是您期望得到的任何内容

© www.soinside.com 2019 - 2024. All rights reserved.