Python paramiko脚本,在exec_command()期间读取输出的问题

时间:2011-10-06 20:54:07

标签: python ssh paramiko

背景: 我正在使用python和paramiko来自动化我每次必须交给一个类的程序时经历的过程。我们使用一个名为“handin”的命令来提交源代码,但这必须从学校的计算机上完成。因此,当我从家中提交代码时,我必须:sftp进入学校服务器,将文件放入目录,ssh进入学校计算机,使用'handin'命令

我可以成功地将文件放到学校的机器上。当我尝试使用exec_command('handin my files')然后读取输出以确定下一个操作时,会出现问题。

所以我有

try:
   (stdin, stdout, stderr) = client.exec_command(s)
except:
   print 'whoops'
   sys.exit()
print stdout.readlines()

但是由于某些原因导致死锁,脚本似乎什么都不做,我最终必须终止整个过程(ctrl + c不起作用)。我不确定exec_command是否未正确完成(即使它已经退出try / catch块),或者我是否有网络问题或者是什么。

有什么想法吗?

更新

问题在于在执行期间与handin命令交互。执行命令后,handin可能仍在运行,也可能不运行。如果它第一次提交它说成功,等等等等,并完成执行。一切都很好。但如果我重新提交,我必须为每个文件授权覆盖(stdin.write('y'))。

TL / DR

如何检查exec_command()是否仍在运行,等待输入,以及相应的stdout readline()?

4 个答案:

答案 0 :(得分:11)

我没有看到上面的代码snippit有什么问题。下面的程序是一个完整的脚本,它登录到主机并运行ls命令来查看文件。我只是尝试过,它对我有用。也许尝试一下,看看它是否适合你。如果它不起作用,我怀疑某个特定于您的ssh服务器,正在运行的命令或paramiko安装的问题。如果它对您有用,那么只需对此进行更改即可转向现有功能并查看其中断位置。

import paramiko
ssh=paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('<ip address here>',username='<username here>',password='<password here>')
stdin,stdout,stderr = ssh.exec_command("ls /")
print stdout.readlines()

如果这对您有用,我的下一个建议是尝试用您尝试运行的实际handin命令替换'ls /'。可能该命令正在等待用户输入等等。

答案 1 :(得分:6)

问题可能是远程命令正在等待输入(它希望你写一些东西给stdin,不知道你不会去,除非你这么说)。试试stdin.channel.shutdown_write()(我相信stdin.close()本身不会起作用:那只会导致同花一次)

答案 2 :(得分:0)

这是一个旧线程,但我也看到了此问题。这是由于Paramiko中的错误导致无法通过远程命令处理大型STDOUT而导致的。

参考:https://github.com/paramiko/paramiko/issues/515

可能的解决方案:https://gist.github.com/matjohn2/2c1c4988e17112a34f310667e0ff6e7e

答案 3 :(得分:0)

好的,所以我最近也被这个问题困住了。检查文档和更多搜索让我得到一个可接受的答案,我可以用它来发送输入并检查可能需要用户输入的简单脚本的输出

而不是尝试使用 .read().readline() 等待直到程序运行完成。 查询他们只要求一个字节,如 .read(1).readline(1) 直到 stdout.channel.recv_ready() 返回 True 表示是否有东西 要读取的标准输出 使用 stdout.channel.recv(9999) 而不是 realine(1) 可能会更好,因为它允许您请求 9999 个字节而不是 1 个读取/读取行,并且如果您请求的字节数超过现有字节数,您将不会陷入死锁.

http://docs.paramiko.org/en/stable/api/channel.html#paramiko.channel.Channel.recv_ready

以下只是样板代码,用于查看执行的基本 sudo 命令

ssh.connect(
        hostname=host,
        port=port,
        username=username,
        password=password,
    )

def readlines(stdout):
    line = ''
    while sout.channel.recv_ready():
        line += sout.readline(1)
    return line


>> stdin, stdout, stderr = ssh.exec_command('sudo ls -al /root', get_pty=True)

>> output = readlines(stdout)

>> print(output)
'[sudo] password for user: '


>> if "[sudo] password for" in output:
    stdin.write(password + '\n')
    stdin.flush()

>> output = readlines(stdout)

>> print(output)
total 88
drwx------  7 root root     14 May  6 21:40 .
drwxr-xr-x 18 root root     24 Apr 29 18:46 ..
-rw-------  1 root root   3619 May  7 13:19 .bash_history


现在您可以使用它基本上将其扩展到您想要的任何检查。使用 invoke_shell 可能有更好的方法来实现这一点,但这对我有用。