连续从 Paramiko SSH exec_command 获取输出
Get output from a Paramiko SSH exec_command continuously
我正在使用 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)
如何实现? 注意:当然可以将输出通过管道传输到一个文件,然后 'less' 通过另一个 ssh 会话传输该文件, 但这非常丑陋,我需要一个更干净的,理想的 pythonic 解决方案 :)
如 read([size]) documentation 中指定的那样,如果您未指定 size
,它会一直读取到 EOF,这会使脚本等到命令结束后再从 read()
返回并打印任何输出。
查看此答案:How to loop until EOF in Python? and How to do a "While not EOF" 了解如何耗尽类文件对象的示例。
我遇到了类似的问题。我能够通过将 get_pty=True 添加到 paramiko 来解决它:
stdin, stdout, stderr = client.exec_command("/var/mylongscript.py", get_pty=True)
如何使用 的最小且完整的工作示例(在 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
使用这个:
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)
从生成器函数流式传输响应数据。
我想制作一个 class,它比标准 Client.exec_command() 示例更复杂,但比我看到的 Channel.exec_command() 示例更复杂。另外,我介绍了我遇到的一些 'gotchas'。此摘要脚本已在 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()
以上答案均使用exec_command
,如果你也使用这种方式,它们会很有用。但是如果你尝试 send()
和 invoke_shell()
它们将没有用。这样,你可以试试这个
while True:
sleep(0.1)
backMsg = ""
try:
backMsg = self.channel.recv(65536).decode('utf-8')
except socket.timeout as e:
break
print 'backMsg:%s, length:%d, channel recv status:%d '%(backMsg, len(backMsg), self.channel.recv_ready())
if len(backMsg) == 0 and self.channel.recv_ready() == False:
break
但这仍然有一个问题,如果通道在超时时间内无法从服务器接收到任何回复,那么即使您仍然想要代码,代码也会中断循环 运行。
我正在使用 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)
如何实现? 注意:当然可以将输出通过管道传输到一个文件,然后 'less' 通过另一个 ssh 会话传输该文件, 但这非常丑陋,我需要一个更干净的,理想的 pythonic 解决方案 :)
如 read([size]) documentation 中指定的那样,如果您未指定 size
,它会一直读取到 EOF,这会使脚本等到命令结束后再从 read()
返回并打印任何输出。
查看此答案:How to loop until EOF in Python? and How to do a "While not EOF" 了解如何耗尽类文件对象的示例。
我遇到了类似的问题。我能够通过将 get_pty=True 添加到 paramiko 来解决它:
stdin, stdout, stderr = client.exec_command("/var/mylongscript.py", get_pty=True)
如何使用
# 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
使用这个:
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)
从生成器函数流式传输响应数据。
我想制作一个 class,它比标准 Client.exec_command() 示例更复杂,但比我看到的 Channel.exec_command() 示例更复杂。另外,我介绍了我遇到的一些 'gotchas'。此摘要脚本已在 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()
以上答案均使用exec_command
,如果你也使用这种方式,它们会很有用。但是如果你尝试 send()
和 invoke_shell()
它们将没有用。这样,你可以试试这个
while True:
sleep(0.1)
backMsg = ""
try:
backMsg = self.channel.recv(65536).decode('utf-8')
except socket.timeout as e:
break
print 'backMsg:%s, length:%d, channel recv status:%d '%(backMsg, len(backMsg), self.channel.recv_ready())
if len(backMsg) == 0 and self.channel.recv_ready() == False:
break
但这仍然有一个问题,如果通道在超时时间内无法从服务器接收到任何回复,那么即使您仍然想要代码,代码也会中断循环 运行。