在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()
解决方法:
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),因为使所有事情都变得很困难,并且不同的OS在边缘情况下的行为也有所不同. (看一下pexpect.spawn.read_nonblocking()可以看到它有多混乱.)
但是,更干净的方法是使用paramiko,它为通过ssh连接进行处理提供了更高层次的抽象.特别是,请看paramiko.client.SSHClient类的示例用法.