【问题标题】:python ssh stdout multiplepython ssh stdout 多个
【发布时间】:2014-05-22 15:33:47
【问题描述】:

在 python 中,我试图通过 ssh 连接并一一播放多个命令。 此代码工作正常,输出打印到我的屏幕:

cmd = ['ssh', '-t', '-t', 'user@host']
p = subprocess.Popen(cmd, stdin=subprocess.PIPE)
p.stdin.write('pwd\n')
p.stdin.write('ls -l\n') 
p.stdin.write('exit\n')   
p.stdin.close()

我的问题是当我尝试获取字符串中的每个响应时。我已经尝试过了,但读取功能被阻塞:

cmd = ['ssh', '-t', '-t', 'user@host']
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
p.stdin.write('pwd\n')
st1 = p.stdout.read()
p.stdin.write('ls -l\n')    
st2 = p.stdout.read()
p.stdin.close()

【问题讨论】:

    标签: python ssh stdout


    【解决方案1】:

    我同意 Alp 的观点,即拥有一个库来为您执行连接逻辑可能更容易。 pexpect 是一种方法。以下是 paramiko 的示例。 http://docs.paramiko.org/en/1.13/

    import paramiko
    
    host = 'myhost'
    port, user, password = '22', 'myuser', 'mypass'
    client = paramiko.SSHClient()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    client.load_system_host_keys()
    client.connect(host, port, user, password, timeout=10)
    
    command = 'ls -l'
    stdin, stdout, stderr = client.exec_command(command)
    errors = stderr.read()
    output = stdout.read()
    client.close()
    

    【讨论】:

      【解决方案2】:

      read() 调用是阻塞的,因为当不带参数调用时,read() 将从相关流中读取,直到遇到 EOF

      如果您的用例与示例代码一样简单,一个廉价的解决方法是将读取 p.stdout 的内容推迟到关闭连接之后:

      cmd = ['ssh', '-t', '-t', 'deploy@pdb0']
      p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
      p.stdin.write('pwd\n')
      p.stdin.write('ls -l\n')
      p.stdin.write('exit\n')    
      p.stdin.close()
      outstr = p.stdout.read()
      

      然后您必须解析 outstr 以分离不同命令的输出。 (寻找远程 shell 提示符可能是最直接的方法。)

      如果您需要在发送另一个命令之前阅读一个命令的完整输出,您会遇到几个问题。首先这可以阻止:

      p.stdin.write('pwd\n')
      st1 = p.stdout.read()
      

      因为您写入p.stdin 的命令可能会被缓冲。您需要在查找输出之前刷新命令:

      p.stdin.write('pwd\n')
      p.stdin.flush()
      st1 = p.stdout.read()
      

      不过,read() 调用仍会阻塞。您要做的是使用指定的缓冲区大小调用read() 并以块的形式读取输出,直到您再次遇到远程shell 提示符。但即便如此,您仍然需要使用select 来检查p.stdout 的状态,以确保您没有阻止。

      有一个名为pexpect 的库可以为您实现该逻辑。使用它会更容易(或者,更好的是,pxssh,它专门用于通过 ssh 连接使用pexpect),因为把一切都做好是相当麻烦的,而且不同的操作系统在边缘情况下的行为会有所不同。 (看看pexpect.spawn.read_nonblocking() 的例子说明它有多混乱。)

      不过,更简洁的方法是使用paramiko,它为通过 ssh 连接做事提供了更高级别的抽象。特别是看paramiko.client.SSHClient类的示例用法。

      【讨论】:

        【解决方案3】:

        感谢两位的回答。为了简单起见,我更新了我的代码:

        def getAnswer(p, cmnd):
            # send my command
            if len(cmnd) > 0:
                p.stdin.write(cmnd + '\n')
                p.stdin.flush()
            # get reply -- until prompt received
            st = ""
            while True:
                char = p.stdout.read(1)
                st += char
                if char == '>':
                    return st
        
        cmd = ['ssh', '-t', '-t', 'user@host']
        p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
        #discard welcome message
        getAnswer(p, '')
        st1 = getAnswer(p, 'pwd')
        st2 = getAnswer(p, 'ls -l')
        ...
        p.stdin.write('exit\n')
        p.stdin.flush()
        p.stdin.close()
        p.stdout.close()
        

        这并不完美,但工作正常。为了检测提示,我只是在等待一个“>”,这可以通过首先发送一个“echo $PS1”并相应地构建一个正则表达式来改进。

        【讨论】:

          猜你喜欢
          • 2013-06-18
          • 2013-06-21
          • 2018-03-16
          • 1970-01-01
          • 1970-01-01
          • 2013-04-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多