![](/img/trans.png)
[英]readline hangs on paramiko.Channel when reading “watch” command output
[英]Paramiko channel stucks when reading large ouput
我有一個代碼,我在遠程 Linux 機器上執行命令並使用 Paramiko 讀取輸出。 代碼 def 如下所示:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(IPAddress, username=user['username'], password=user['password'])
chan = self.ssh.get_transport().open_session()
chan.settimeout(10800)
try:
# Execute thecommand
chan.exec_command(cmd)
contents = StringIO.StringIO()
data = chan.recv(1024)
# Capturing data from chan buffer.
while data:
contents.write(data)
data = chan.recv(1024)
except socket.timeout:
raise socket.timeout
output = contents.getvalue()
return output,chan.recv_stderr(600),chan.recv_exit_status()
上面的代碼適用於小輸出,但它會卡住更大的輸出。
這里有任何與緩沖區相關的問題嗎?
我正在發布與布魯斯·韋恩 (Bruce Wayne) 的輸入一起使用的最終代碼(:))
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(IPAddress, username=user['username'], password=user['password'])
chan = self.ssh.get_transport().open_session()
chan.settimeout(10800)
try:
# Execute the given command
chan.exec_command(cmd)
# To capture Data. Need to read the entire buffer to capture output
contents = StringIO.StringIO()
error = StringIO.StringIO()
while not chan.exit_status_ready():
if chan.recv_ready():
data = chan.recv(1024)
#print "Indside stdout"
while data:
contents.write(data)
data = chan.recv(1024)
if chan.recv_stderr_ready():
error_buff = chan.recv_stderr(1024)
while error_buff:
error.write(error_buff)
error_buff = chan.recv_stderr(1024)
exit_status = chan.recv_exit_status()
except socket.timeout:
raise socket.timeout
output = contents.getvalue()
error_value = error.getvalue()
return output, error_value, exit_status
我認為沒有與 stdout 通道相關的問題,但我不確定您處理 stderr 的方式。 你能確認,這不是導致問題的 stderr 捕獲嗎? 我會嘗試你的代碼並讓你知道。
更新:當您執行的命令在 STDERR 中提供大量消息時,您的代碼會凍結。 我不確定為什么,但recv_stderr(600)
可能是原因。 因此,捕獲錯誤流的方式與捕獲標准輸出的方式相同。 就像是,
contents_err = StringIO.StringIO()
data_err = chan.recv_stderr(1024)
while data_err:
contents_err.write(data_err)
data_err = chan.recv_stderr(1024)
您甚至可以先嘗試將recv_stderr(600)
更改為recv_stderr(1024)
或更高版本。
其實我認為以上所有答案都不能解決真正的問題:
如果遠程程序首先產生大量的stderr 輸出,那么
stdout.readlines()
stderr.readlines()
會永遠掛着。 雖然
stderr.readlines()
stdout.readlines()
將解決這種情況,但如果遠程程序首先產生大量stdout 輸出,它將失敗。
我還沒有解決辦法...
如果您使用開放 ssh 會話的高級表示會更容易。 由於您已經使用ssh-client打開您的頻道,您可以從那里運行您的命令,並避免額外的工作。
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(IPAddress, username=user['username'], password=user['password'])
stdin, stdout, stderr = ssh.exec_command(cmd)
for line in stdout.readlines():
print line
for line in stderr.readlines():
print line
如果您之后收到其他數據,您將需要返回並再次讀取這些文件句柄。
TL; DR:呼叫stdout.readlines()
之前stderr.readlines()
如果使用ssh.exec_command()
如果您使用@Spencer Rathbun 的回答:
sh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(IPAddress, username=user['username'], password=user['password'])
stdin, stdout, stderr = ssh.exec_command(cmd)
您可能希望了解大輸出可能帶來的限制。
實驗上, stdin, stdout, stderr = ssh.exec_command(cmd)
將無法立即將完整輸出寫入stdout
和stderr
。 更具體地說,緩沖區在填滿之前似乎可以容納2^21
(2,097,152) 個字符。 如果任何緩沖區已滿, exec_command
將阻止寫入該緩沖區,並保持阻塞狀態,直到該緩沖區被清空到足以繼續。 這意味着如果您的stdout
太大,您將繼續讀取stderr
,因為在可以寫入完整輸出之前,您不會在任一緩沖區中收到 EOF。
解決此問題的簡單方法是 Spencer 使用的方法 - 在嘗試讀取stderr
之前通過stdout.readlines()
獲取所有正常輸出。 只有當stderr
有超過2^21
字符時,這才會失敗,這在我的用例中是可以接受的限制。
我發布這個主要是因為我很笨,花了很長時間試圖弄清楚我是如何破壞我的代碼的,當答案是我在stdout
之前從stderr
讀取並且我的stdout
太大而無法放入緩沖。
要讓 paramiko 命令表現得像 subprocess.call,你可以使用這段代碼(用 python-3.5 和 paramiko-2.1.1 測試):
#!/usr/bin/env /usr/bin/python3
import os
import sys
from paramiko import SSHClient, AutoAddPolicy
from socket import getfqdn
class SecureSHell(object):
reuser = os.environ['USER']
remote = ''
def __init__(self, *args, **kwargs):
for arg in args:
if hasattr(self, arg):
setattr(self, arg, True)
for (key, val) in kwargs.items():
if hasattr(self, key):
setattr(self, key, val)
@staticmethod
def _ssh_(remote, reuser, port=22):
if '@' in remote:
_reuser, remote = remote.split('@')
_fqdn = getfqdn(remote)
remote = _fqdn if _fqdn else remote
ssh = SSHClient()
ssh.set_missing_host_key_policy(AutoAddPolicy())
ssh.connect(remote, int(port), username=reuser)
return ssh
def call(self, cmd, remote=None, reuser=None):
remote = remote if remote else self.remote
reuser = reuser if reuser else self.reuser
ssh = self._ssh_(remote, reuser)
chn = ssh.get_transport().open_session()
chn.settimeout(10800)
chn.exec_command(cmd)
while not chn.exit_status_ready():
if chn.recv_ready():
och = chn.recv(1024)
while och:
sys.stdout.write(och.decode())
och = chn.recv(1024)
if chn.recv_stderr_ready():
ech = chn.recv_stderr(1024)
while ech:
sys.stderr.write(och.decode())
ech = chn.recv_stderr(1024)
return int(chn.recv_exit_status())
ssh = SecureSHell(remote='example.com', user='d0n')
ssh.call('find')
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.