从paramiko ssh exec_command连续获取输出

时间:2015-08-05 14:02:01

标签: python ssh stdout paramiko interactive

我正在使用paramiko在远程机器上通过ssh执行长时间运行的python脚本。工作就像一个魅力,到目前为止没有问题。

不幸的是,stdout(分别是stderr)只在脚本完成后显示!但是,由于执行时间的原因,我更倾向于在打印时输出每个新行,而不是之后。

remote = paramiko.SSHClient()
remote.set_missing_host_key_policy(paramiko.AutoAddPolicy())
remote.connect("host", username="uname", password="pwd")

# myScript produces continuous output, that I want to capture as it appears    
stdin, stdout, stderr = remote.exec_command("python myScript.py")
stdin.close()
for line in stdout.read().splitlines():
    print(line)

如何实现? 注意:当然可以通过另一个ssh会话将输出传输到文件并“减少”此文件,但这非常难看,我需要更清洁,理想的pythonic解决方案:)

5 个答案:

答案 0 :(得分:10)

read([size]) documentation中所述,如果您没有指定size,则会在EOF之前读取,这会使脚本等到命令结束后再从read()返回并打印任何输出。

请查看以下答案:How to loop until EOF in Python?How to do a "While not EOF",了解如何耗尽类似文件的对象。

答案 1 :(得分:7)

我遇到了类似的问题。我能够通过向paramiko添加get_pty = True来解决它:

stdin, stdout, stderr = client.exec_command("/var/mylongscript.py", get_pty=True)

答案 2 :(得分:7)

如何使用this answer(在Python 3.6.1中测试)的最小且完整的工作示例

# run.py
from paramiko import SSHClient

ssh = SSHClient()
ssh.load_system_host_keys()

ssh.connect('...')

print('started...')
stdin, stdout, stderr = ssh.exec_command('python -m example', get_pty=True)

for line in iter(stdout.readline, ""):
    print(line, end="")
print('finished.')

# example.py, at the server
import time

for x in range(10):
    print(x)
    time.sleep(2)

使用

在本地计算机上运行
python -m run

答案 3 :(得分:2)

来自生成器函数的流式响应数据。

我想创建一个比标准 Client.exec_command() 示例更复杂且比我在 Channel.exec_command() 示例中看到的更复杂的类。另外,我还介绍了我遇到的一些“陷阱”。此摘要脚本已在 CentOS Stream - Python 3.6.8 上测试。

import sys
import paramiko

client = paramiko.SSHClient()
client.load_system_host_keys('/etc/ssh/ssh_known_hosts')

try:
    client.connect('host', username='username', password='password',
        port=22, timeout=2)
except Exception as _e:
    sys.stdout.write(_e)

# is_active can be a false positive, so further test
transport = client.get_transport()
if transport.is_active():
    try:
        transport.send_ignore()
    except Exception as _e:
        sys.stdout.write(_e)
        sys.exit(1)
else:
   sys.exit(1)

channel = transport.open_session()
# We're not handling stdout & stderr separately
channel.set_combine_stderr(1)
channel.exec_command('whoami')
# Command was sent, no longer need stdin
channel.shutdown_write()

def responseGen(channel):
    # Small outputs (i.e. 'whoami') can end up running too quickly
    # so we yield channel.recv in both scenarios
    while True:
        if channel.recv_ready():
            yield channel.recv(4096).decode('utf-8')

        if channel.exit_status_ready():
            yield channel.recv(4096).decode('utf-8')
            break

# iterate over each yield as it is given 
for response in responseGen(channel):
    sys.stdout.write(response)

# We're done, explicitly close the conenction
client.close()

答案 4 :(得分:1)

使用此:

stdin, stdout, stderr = ssh.exec_command('python -m example', get_pty=True)

for line in iter(stdout.readline, ""):
    print(line, end="")

来自@JorgeLeitao的答案使我的stdout输出加速到几乎实时!

我正在使用:

stdin, stdout, stderr = ssh.exec_command(cmd)
for line in stdout:
    # Process each line in the remote output
        print (line)