10

我有一个代码,我在远程 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()

上面的代码适用于小输出,但对于较大的输出会卡住。

这里有任何与缓冲区相关的问题吗?

4

6 回答 6

8

我正在发布与布鲁斯韦恩(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
于 2013-02-12T12:55:49.727 回答
3

我看不出与标准输出通道相关的问题,但我不确定您处理标准错误的方式。您能否确认,它不是导致问题的标准错误捕获?我会尝试你的代码并让你知道。

更新:当您执行的命令在 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)或更高。

于 2013-02-08T10:14:59.573 回答
2

其实我认为以上所有答案都不能解决真正的问题:

如果远程程序首先产生大量的标准错误输出然后

stdout.readlines()
stderr.readlines()

会永远挂着。虽然

stderr.readlines()
stdout.readlines()

将解决这种情况,但如果远程程序首先产生大量标准输出输出,它将失败。

我还没有解决办法...

于 2014-02-13T03:29:42.313 回答
1

如果您使用开放 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

如果您之后收到其他数据,您将需要返回并再次从这些文件句柄中读取。

于 2013-02-11T15:18:25.980 回答
1

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)将无法立即将完整输出写入stdoutstderr2^21更具体地说,缓冲区在填满之前似乎可以保存(2,097,152) 个字符。如果任何缓冲区已满,exec_command将阻止写入该缓冲区,并将保持阻塞状态,直到该缓冲区被清空到足以继续。这意味着如果你stdout的文件太大,你会一直挂在 read 上stderr,因为在它可以写入完整输出之前,你不会在任何一个缓冲区中收到 EOF。

解决此问题的简单方法是 Spencer 使用的方法 -stdout.readlines()在尝试读取之前通过获取所有正常输出stderr。仅当您在 中包含多个2^21字符时stderr,这才会失败,这在我的用例中是可以接受的限制。

I'm mainly posting this because I'm dumb and spent far, far too long trying to figure out how I broke my code, when the answer was that I was reading from stderr before stdout and my stdout was too big to fit in the buffer.

于 2020-01-07T21:00:48.453 回答
0

要让 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')                                                           
于 2017-01-15T20:25:34.410 回答