设计意图:初始化一次spawn对象,执行ssh后下发,一系列cmds。网上大部分时通过ssh root@ip cmd的方法,这样的话,我就之需要通过for调用:

def SSH_COMMANDS(ip,user,passwd,command):
    try:
        ssh_pc = pexpect.spawn('ssh %s@%s %s' % (user,ip,command),timeout=5,logfile=logfile)
        ...
        r=ssh_pc.read()
        print r
    except TIMEOUT,EOF:
        ssh_pc.close()
        ...
       
for cmd in cmds:
    SSH_COMMANDS(ip,user,passwd,cmd)

但是,每次for都要生成spawn对象,所以,我的方法是:

def SSH_COMMANDS(ip,user,passwd,commands):
    try:
        ssh_pc = pexpect.spawn('ssh %s@%s' % (user,ip),timeout=5,logfile=logfile)
        ...
        for command in commands:
            ssh_pc.expect('#')
            ssh_pc.sendline(command)
        r=ssh_pc.read()
            print r
    except TIMEOUT,EOF:
        ssh_pc.close()
        ...
SSH_COMMANDS(ip,user,passwd,cmds)

我的code如下:

import pexpect
import sys
def SSH_COMMANDS(ip,user,passwd,commands):
    ip=str(ip)
    user=str(user)
    passwd=str(passwd)
    #logfile=file('%s_%s' % (ip,user),'w')
    logfile=sys.stdout
    ssh_pc = pexpect.spawn('ssh %s@%s' % (user,ip),timeout=5,logfile=logfile)
    try:            
    match_tag = ssh_pc.expect(['continue connecting (yes/no)?','password:'],timeout=3)
    if match_tag==0:
        print '#'*30
        ssh_pc.sendline('yes')
        ssh_pc.expect('password:')
        ssh_pc.sendline(passwd)
    
    elif match_tag==1:
        print '='*30
        ssh_pc.sendline(passwd)
    
    for cmd in commands:
        ssh_pc.expect(['#',pexpect.TIMEOUT,pexpect.EOF])
        ssh_pc.sendline(cmd)
    ssh_pc.expect(['#',pexpect.TIMEOUT,pexpect.EOF])
    ssh_pc.close
    #ssh_pc.sendline('exit')    
    print ssh_pc.read()
    except pexpect.EOF:  
        print 'sub process abort unfortinately'
    ssh_pc.close
    except pexpect.TIMEOUT:
        print 'expect patterns failed'
    ssh_pc.close
SSH_COMMANDS('172.172.1.1','root','fortinet',['uname -a','hostname'])

运行结果:


root@172.172.1.1's password: ==============================
fortinet
 
Welcome to Ubuntu 12.04.4 LTS (GNU/Linux 3.5.0-23-generic x86_64)
 
 * Documentation:  https://help.ubuntu.com/
 
  System information as of Tue Oct 21 11:50:25 CST 2014
 
  System load:  0.08               Users logged in:     2
  Usage of /:   12.4% of 28.81GB   IP address for eth0: 172.172.1.1
  Memory usage: 5%                 IP address for eth1: 10.1.1.1
  Swap usage:   0%                 IP address for eth2: 10.16.1.2
  Processes:    218
 
  Graph this data and manage this system at:
    https://landscape.canonical.com/
 
175 packages can be updated.
118 updates are security updates.
 
New release '14.04.1 LTS' available.
Run 'do-release-upgrade' to upgrade to it.
 
You have new mail.
Last login: Tue Oct 21 11:05:45 2014 from 172.172.4.1 
root@DEV-1-A:~# uname -a
uname -a
Linux DEV-1-A 3.5.0-23-generic #35~precise1-Ubuntu SMP Fri Jan 25 17:13:26 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
root@DEV-1-A:~# hostname
hostname
DEV-1-A 


root@DEV-1-A:~# expect patterns failed 这里匹配到pexpect.TIMEOUT分支。网上查了很多资料,也看了别人写的程序,他们是在初始化对象时,把cmd加到了pexpect.spawn的command参数,即:

ssh_pc = pexpect.spawn('ssh %s@%s %s' % (user,ip,cmd),timeout=5,logfile=logfile)  ,我这样试过,确实可以规避read()抛出的异常,然后把整个SSH_COMMANDS函数放到一个for循环也可以实现传递一个list类型的cmds。但是这相当于每次要初始化一个spawn类对象。而我,希望初始化一次spawn类对象,通过expect和sendline循环使用,下发list类型的cmds。    

然而,当我取消注释下面一行后,TIMEOUT异常消失了。
ssh_pc.sendline('exit')

执行通过的code如下:

import pexpect
import sys
def SSH_COMMANDS(ip,user,passwd,commands):
    ip=str(ip)
    user=str(user)
    passwd=str(passwd)
    #logfile=file('%s_%s' % (ip,user),'w')
    logfile=sys.stdout
    ssh_pc = pexpect.spawn('ssh %s@%s' % (user,ip),timeout=5,logfile=logfile)
    try:            
    match_tag = ssh_pc.expect(['continue connecting (yes/no)?','password:'],timeout=3)
    if match_tag==0:
        print '#'*30
        ssh_pc.sendline('yes')
        ssh_pc.expect('password:')
        ssh_pc.sendline(passwd)
    
    elif match_tag==1:
        print '='*30
        ssh_pc.sendline(passwd)
    
    for cmd in commands:
        ssh_pc.expect(['#',pexpect.TIMEOUT,pexpect.EOF])
        ssh_pc.sendline(cmd)
    ssh_pc.expect(['#',pexpect.TIMEOUT,pexpect.EOF])
    ssh_pc.close
    ssh_pc.sendline('exit')    
    print ssh_pc.read()
    except pexpect.EOF:  
        print 'sub process abort unfortinately'
    ssh_pc.close
    except pexpect.TIMEOUT:
        print 'expect patterns failed'
    ssh_pc.close
SSH_COMMANDS('172.172.1.1','root','fortinet',['uname -a','hostname'])


执行结果:

root@172.172.1.1's password: ==============================
fortinet
 
Welcome to Ubuntu 12.04.4 LTS (GNU/Linux 3.5.0-23-generic x86_64)
 
 * Documentation:  https://help.ubuntu.com/
 
  System information as of Tue Oct 21 12:50:53 CST 2014
 
  System load:  0.0                Users logged in:     2
  Usage of /:   12.4% of 28.81GB   IP address for eth0: 172.172.1.1
  Memory usage: 5%                 IP address for eth1: 10.1.1.1
  Swap usage:   0%                 IP address for eth2: 10.16.1.2
  Processes:    218
 
  Graph this data and manage this system at:
    https://landscape.canonical.com/
 
175 packages can be updated.
118 updates are security updates.
 
New release '14.04.1 LTS' available.
Run 'do-release-upgrade' to upgrade to it.
 
You have new mail.
Last login: Tue Oct 21 11:50:25 2014 from 172.22.4.7  
root@DEV-1-A:~# uname -a
uname -a
Linux DEV-1-A 3.5.0-23-generic #35~precise1-Ubuntu SMP Fri Jan 25 17:13:26 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
root@DEV-1-A:~# hostname
exit
hostname
DEV-1-A
root@DEV-1-A:~# exit
logout
Connection to 172.172.1.1 closed.  
 hostname
DEV-1-A
root@DEV-1-A:~# exit
logout
Connection to 172.172.1.1 closed.  

想不明白一个问题,我已经ssh_pc.read()前执行ssh_pc.close(),为什么一定要再发送个ssh_pc.sendline('exit')才能规避异常TIMEOUT。请明白的大神指点一下。谢谢。