Python ssh中输入密码and模拟键盘输入

ziDANe 撰写  

最近写一个脚本,需要SSH到一个设备上,基于linux的,并在该设备上进入特权模式,这里用这样一样例子来演示,普通用户登录到ssh,然后切换root用户,这是会提示再次输入密码,就是这样一个效果。折腾半天,总算搞定了,把试过的各种方法给大家分享下。主要尝试了paramiko 以及 pexpect这两个库。

Paramiko之SSHClient()

起初用的Paramiko中的SSHClient()类来实现,连接到SSH并执行操作很简单:


#!/usr/bin/python
import paramiko
hostname= '192.168.73.137'
port = 22
username='man'
password='d-up.org'
if __name__=="__main__":
	paramiko.util.log_to_file('paramiko.log')
	s = paramiko.SSHClient()
	s.load_system_host_keys()
	s.connect(hostname,port,username,password)
        stdin,stdout,stderr=s.exec_command('whoami')
	ss=stdout.read()
	print ss
        stdin,stdout,stderr=s.exec_command('su root')
        stdin,stdout,stderr=s.exec_command('rootpass)   ##这里不能成功发送命令
         ……
         ……
	s.close()

这时是用exec_command来执行命令,起初我以为字符串+回车,因此认为当su root 后直接发送密码过去便可以成功登录,后来发现这里的执行命令不是简单的发送字符串+回车,这样写是无法成功的,于是我查看了下SSHClient类,没有发现可以执行此类操作的函数。多次研究后不成功,只好放弃。

Pexpect之pxssh

Pexpect是一个很强大的库,可以用来创建子进程并与其通信。我找到它是因为它也包含一个ssh对象:pxssh,而且他有一个函数sendline(),看到这个函数就觉得这次应该成功了,查了下,这个函数的工作方法如同上面我想的那样,发送”command + \n”。因此很轻松的就实现了我需求的功能。实现所用代码如下:


#!/usr/bin/env python
# -*- coding:utf-8 -*-
import pxssh   #pxssh 位于pexpect中
try:
    # 调用构造函数,创建一个 pxssh 类的对象.
    s = pxssh.pxssh()
    hostname = '192.168.73.126'
    username = 'man'
    password = 'd-up.org'
    # 利用 pxssh 类的 login 方法进行 ssh 登录,原始 prompt 为'$' , '#'或'>',这个方法不用指定端口
    s.login (hostname, username, password, original_prompt='[$#>]')
    s.sendline ('whoami')
    # 匹配 prompt
    s.prompt()
    # 将 prompt 前所有内容打印出,即命令 'whoami' 的执行结果.
    print s.before
    # 发送命令 ' ls -l '
    p2='root_pass'
    s.sendline ('su root')   #此时不需要prompt,如果我们直接操作的话会看到提示输入密码
     x=s.expect(['口令: '])   # 关键expect期待后面数组中的内容,并进行匹配,这里由于我使用的中文GUN Linux,所以我希望他匹配 提示中的口令两个字

     if x==0:     #0是 expect匹配数组中“口令”的位置,这里只有一个,所以是0
       s.sendline(p2)   # 匹配成功后发送的命令
        s.prompt()
       print s.before       #这是就可以prompt了,因为su成功后有  #的提示符
        print 'su ok'
    s.sendline('whoami')
    s.prompt()
    print s.before     #应该可以看到身份是root  模拟键盘提交密码成功!
    # 退出 ssh session
    s.sendline('exit')
    s.logout()
except pxssh.ExceptionPxssh, e:
    print "pxssh failed on login."
    print str(e)

我开始测试代码的时候是用自己的系统做的测试,一切工作正常,但是后面发生意外了,当我去连接该设备的时候,出现了错误,pexpect库报错,现在我也没有找到原因,用多台设备进行测试均无法正常工作。E文不太好,最后没有解决。只好在回到paramiko身上,因为大家都说他很强大。

Paramiko之Transport

最后的实现方法,Transport类,这个类更加通用,说明是这样的(我就不翻译了,免得丢人):An SSH Transport attaches to a stream (usually a socket), negotiates an encrypted session, authenticates, and then creates stream tunnels, called Channels, across the session. Multiple channels can be multiplexed across a single session (and often are, in the case of port forwardings).
该类有send()方法,其工作原理更简单,直接发送字符串,回车还得自己补上,千万别忘记了,这个十分好用,代码很清晰,最终在那个设备上完成了任务。实现代码如下,大家看看应该也就明白了。


import paramiko
s = paramiko.Transport(('192.168.73.126', 22))    #创建对象
s.connect(username='man', password='d-up.org')   #连接
chan = s.open_session()     #得到session
chan.get_pty()                  #Request a pseudo-terminal from the server.    这两行实在翻译不好,大家自己看看吧
chan.invoke_shell()             #Request an interactive shell session on this channel.
chan.send('su root\n')         #逐行发送命令,如果进行的操作复杂,最好等上几秒,或者通过接受到的数据来进行判断,然后再执行后面的命令
chan.send('rootpass\n')       #换行别忘记发
chan.send('whoami\n')        #www.d-up.org/man python爱好者
print chan.recv(1024)    # 可以看到切换用户成功了,大功告成!

2 条评论

  1. 发表了 2010年01月22日 在 5:22 下午 | 永久链接

    小波温馨提示:注意劳逸结合,合理安排作息时间,晚饭时间快到了!祝您周末愉快!

  2. Toad
    发表了 2010年04月6日 在 11:08 上午 | 永久链接

    最好加个time.sleep

发表评论

Your email is never shared.