Expect是一个用来处理交互的命令。借助Expect,我们可以将交互过程写在一个脚本上,使之自动化完成。形象的说,ssh登录,
ftp登录等都符合交互的定义。

Expect中最关键的四个命令是send,expect,spawn,interact。

send:用于向进程发送字符串
expect:从进程接收字符串
spawn:启动新的进程
interact:允许用户交互

================================================================
2.7版本以上的Python:
pip install pyexpect

装好pyexpect之后:
In [1]: import pexpect

In [2]: output,exitstatus = pexpect.run('ls -l',withexitstatus=1)

In [3]: print output,exitstatus

解释:
以上的output和exitstatus这两个变量名字是自己定义的
'ls -l'的执行结果赋值给前面的变量output
withexitstatus=1是开启返回状态,然后把这个返回状态赋值给前面的变量exitstatus

================================================================
接下来更深入的学习一下spawn,它能帮我们解决更加复杂的交互问题
先概念性的记住以下方法:
1、产生子进程执行相关命令pexpect.spawn()返回一个子进程实例:
child = pexpect.spawn(‘command’)

2、该实例产生的日志信息可以通过child.logfile进行配置:(可选项)
f = file(’/tmp/plog.out’, ‘w’)
child.logfile = f

3、使用expect进行交互
index = child.expect([“命令执行成功输出的内容”,”命令执行失败输出的内容”pexpect.EOF,pexpect.TIMEOUT]) 
pexpect.EOF 子进程结束
pexpect.TIMEOUT 超时,默认30s
然后通过if判断index的值,当index为0代表出现了“命令执行成功输出的内容”,当命令为1时代表”命令执行失败输出的内容”

4、通过sendline给子进程发送信息,自动带一个回车:
child.sendline(‘command’)
#甚至可以通过sendcontrol(‘c’)来发送ctrl+c

5、使用try,except来进行异常处理
try:
    index = p.expect(['good', 'bad'])
    if index == 0:
        do_something()
    elif index == 1:
        do_something_else()
except EOF:
    do_some_other_thing()
    except TIMEOUT:
    do_something_completely_different()

6、可以使用before来打印命令执行结果:
p = pexpect.spawn('/bin/ls')
p.expect(pexpect.EOF)
print p.before

以上6点我觉得可以让你游刃有余的解决日常90%进程交互的问题。
这里share一个经验就是写程序之前,先把这6个问题思考好,定义好,再去写程序的主题就ok了!

================================================================
例1:
现在有一台ftp服务器,地址为192.168.100.194,我要连上去,并且下载一个名为passwd的文件,然后退出。这个操作可以手动操作,
但如果工作量大了,我们就可以写个脚本来操作:

#!/usr/bin/env python

import pexpect
import sys

child = pexpect.spawn ('ftp 192.168.100.194')
child.expect ('Name .*: ')
child.sendline (sys.argv[1])    #这里是传的的一个参数,即下文的hello
child.expect ('Password:')
child.sendline (sys.argv[2])    #这里是第二个参数,即下文的123
child.expect ('ftp> ')
child.sendline ('get passwd')   #下载passwd这个文件
child.expect('ftp> ')
child.sendline ('bye')          #退出

执行结果:
[root@localhost test]# python a.py hello 123
[root@localhost test]# ls
a.py  passwd

例1修改版:
import pexpect

username = 'ptest'
key = '123456'
f = file('/tmp/plog.out', 'w')

if __name__ == "__main__":
    child = pexpect.spawn('ftp localhost') #产生新的子进程对象
    child.logfile = f                      #指定日志文件为上面的变量f
try:
    index = child.expect(['Name'])         #期待命令执行成功并输出Name
    if index == 0:                           #如果是列表里的第0个,即Name,则往下走
        child.sendline(username)           #通过sendline给子进程发送信息,自带回车
        index = child.expect(['Password']) #期待命令执行成功并输出Password
        if index == 0:
            child.sendline(key)            #如果命中上面的Pwassword,则发送key
            index = child.expect(['Login successful.*ftp>'])
            if index == 0:
                child.sendline('ls')
                index = child.expect(['test.mp3'])
                if index == 0:
                    child.sendline('bin')  #改为二进制,防止下面get的时候乱码
                    child.sendline('get test.mp3')   #下载test.mp3文件
                    index = child.expect(['Transfer complete.*ftp>'])
                    if index == 0:
                        print 'download complete!'
                        child.sendline('bye')  #退出
except:
print child.before
print 'see /tmp/plog.out can find more info!'

执行一下这段代码,然后去查看一下/tmp/plog.out文件的内容
这段代码虽然非常丑陋,但还是能起到练习的作用,稍加变化就能满足您生产需求!

================================================================
例2:线上实例,可拿去用。生成公钥并拷贝到指定主机,支持多台。
#!/usr/bin/env python
#coding=utf8

import pexpect

#指定本地公钥所在的家目录
home = '/root'
#指定远程主机的用户、IP和密码,多台用逗号隔开
info = {'[email protected]':'123456'
       }

f = file('/tmp/plog.out', 'w')

def genkey():
    child = pexpect.spawn("ssh-keygen -t rsa")
    child.logfile = f
    try:
        index = child.expect(['save the key'])
        if index == 0:
            child.sendline()
            index = child.expect(['Enter passphrase', 'Overwrite'])
            if index == 0:
                child.sendline()
                index = child.expect(['Enter same passphrase'])
                if index == 0:
                    child.sendline()
            else:
                child.sendline('n')
    except:
        print child.before

def copykey():
    try:
        for k,v in info.items():
            child = pexpect.spawn("ssh-copy-id -i %s/.ssh/id_rsa.pub %s"%(home,k))
            child.logfile = f
            index = child.expect(['continue connecting','password'])
            if index == 0:
                child.sendline('yes')
            else:
                child.sendline(v)
                child.expect(pexpect.EOF)
    except:
        print child.before

if __name__ == "__main__":
    genkey()
    copykey()

================================================================
例3:这里是ssh登录的过程,有超时,ssh_newkey,让你输入password三种情况:

#!/usr/bin/env python
#encoding=utf8

import pexpect
import getpass, os

#user: ssh 主机的用户名
#host:ssh 主机的域名
#password:ssh 主机的密码
#command:即将在远端 ssh 主机上运行的命令
def ssh_command (user, host, password, command):
    """
    This runs a command on the remote host. This could also be done with the
    pxssh class, but this demonstrates what that class does at a simpler level.
    This returns a pexpect.spawn object. This handles the case when you try to
    connect to a new host and ssh asks you if you want to accept the public key
    fingerprint and continue connecting.
    """
    ssh_newkey = 'Are you sure you want to continue connecting'
    # 为 ssh 命令生成一个 spawn 类的子程序对象.
    child = pexpect.spawn('ssh -l %s %s %s'%(user, host, command))
    i = child.expect([pexpect.TIMEOUT, ssh_newkey, 'password: '])
    # 如果登录超时,打印出错信息,并退出.
    if i == 0: # Timeout
        print 'ERROR!'
        print 'SSH could not login. Here is what SSH said:'
        print child.before, child.after
        return None
    # 如果 ssh 没有 public key,接受它.
    if i == 1: # SSH does not have the public key. Just accept it.
        child.sendline ('yes')
        child.expect ('password: ')
        i = child.expect([pexpect.TIMEOUT, 'password: '])
        if i == 0: # Timeout
            print 'ERROR!'
            print 'SSH could not login. Here is what SSH said:'
            print child.before, child.after
            return None
    # 输入密码.
    child.sendline(password)
    return child

def main ():
    # 获得用户指定 ssh 主机域名.
    host = raw_input('Hostname: ')
    # 获得用户指定 ssh 主机用户名.
    user = raw_input('User: ')
    # 获得用户指定 ssh 主机密码.
    password = getpass.getpass()
    # 获得用户指定 ssh 主机上即将运行的命令.
    command = raw_input('Enter the command: ')
    child = ssh_command (user, host, password, command)
    # 匹配 pexpect.EOF
    child.expect(pexpect.EOF)
    # 输出命令结果.
    print child.before

if __name__ == '__main__':
    try:
        main()
    except Exception, e:
        print str(e)
        os._exit(1)


1.密钥
用shell下的except,这个不好用,ssh可以
#!/usr/bin/env python
# encoding: UTF-8
"""
ssh-copy-id -i /root/.ssh/id_rsa.pub root@localhost
"""
import pexpect
keyfile='/root/.ssh/id_rsa.pub'
target_user='root'
target_host='localhost'
command = 'ssh-copy-id -i ' + keyfile + ' ' + target_user + '@' + target_host
password = '123456'
child = pexpect.spawn(command)
try:
    index = child.expect(['yes/no', 'password'])
    if index == 0:
        child.sendline('yes')
        index = child.expect(['password'])
        if index == 0:
            child.sendline(password)
    else:
        child.sendline(password)
except pexpect.EOF:
    print 'now to try U sshkey'
2、
#!/usr/bin/env python
#coding:utf-8
import pexpect,sys
child = pexpect.spawn("ssh [email protected]")
# pexpect.spawn(command, args=[], timeout=30, maxread=2000, searchwindowsize=None, logfile=None, cwd=None, 
env=None, ignore_sighup=True)
# 其中command参数可以是任意已知的系统命令 参数timeout为等待结果的超时时间;参数maxread为pex-pect从终端控制台一次读取的最大字节数,
searchwindowsize参数为匹配缓冲区字符串的位置,默认是从开始位置匹配。
#需要注意的是,pexpect不会解析shell命令当中的元字符,包括重定向“>”、管道“|”或通配符“*”,当然,我们可以通过一个技巧来解决这个问题,
将存在这三个特殊元字符的命令作为/bin/bash的参数进行调用,
#例如:child = pexpect.spawn('/bin/bash -c "ls -l | grep LOG > logs.txt"')
#      child.expect(pexpect.EOF)
#我们可以通过将命令的参数以Python列表的形式进行替换,从而使我们的语法变成更加清晰,下面的代码等价于上面的。
#       shell_cmd = 'ls -l | grep LOG > logs.txt'
#       child = pexpect.spawn('/bin/bash', ['-c', shell_cmd])
#       child.expect(pexpect.EOF)
fout = file("mylog.txt",'w')
#child.logfile = fout  #输出到文件
child.logfile = sys.stdout  #标准输出,当前终端
passwd = "1"
comm = "free -m"
try:
    index = child.expect(["yes/no","password:"]) #匹配,返回索引值
    if index == 0:
        child.sendline("yes")
        child.expect("password:")
        child.sendline(passwd)
        child.expect('#')
        child.sendline(comm)
        child.expect('#')
        child.sendline("exit")
    elif index == 1:
        child.sendline(passwd)
        child.expect('#')
        child.sendline(comm)
        child.expect('#')
        child.sendline("exit")
    else:
        print "Error"
except Exception,e:
    print str(e)

results matching ""

    No results matching ""