Python SSH标准输出多行

3
在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()

我的问题是当我尝试在一个字符串中获取每个响应时。我已经尝试过这个方法,但read函数会阻塞:

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()
3个回答

2

我同意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()

1
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的库可以为你实现这个逻辑。使用它(或者更好的是专门用于ssh连接的pxssh,它将pexpect优化了一些),会更容易,因为要做到一切正确相当棘手,在边缘情况下不同的操作系统行为也可能有所不同。(看一下pexpect.spawn.read_nonblocking()的例子,就知道有多混乱了。)
更加简洁的方法是使用paramiko,它提供了在ssh连接上执行操作的更高级抽象。特别地,请查看paramiko.client.SSHClient类的示例用法。

1
感谢你们两位的回答。为了简单起见,我已经更新了我的代码,具体如下:
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”并相应地构建正则表达式来改进。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接