Python pexpect模块及Shell脚本Expect原理解析
本文深入解析Linux下的Expect脚本原理及其在Python中的pexpect模块应用。详细讲解send、expect、spawn和interact四大核心命令,通过SSH自动登录、FTP文件下载等实例,展示如何利用模式-动作机制实现自动化交互。同时介绍pexpect中spawn类、pxssh类及run函数的用法,帮助开发者掌握自动化运维与测试技巧。
Expect脚本
Expect脚本概述与基础命令
Expect概述
Expect是一款免费编程工具,专门用于实现无需人工干预的自动交互式任务。简单来说,它是一套用于实现自动交互功能的软件工具。
在实际工作场景中,运行命令、脚本或程序时,往往需要从终端输入继续运行的指令,这些操作通常需人工完成。利用Expect,可以根据程序提示模拟标准输入,从而实现自动化交互执行。
由于Linux中部分命令(如fdisk、telnet、FTP连接下载等)不适合直接用于脚本自动化运行,因此必须借助Expect来解决交互问题。
Expect基础
Expect主要包含以下四个命令:
| 命令 | 作用 |
|---|---|
| send | 用于向进程发送字符串 |
| except | 从进程接收字符串 |
| spwan | 启动新进程 |
| interact | 允许用户交互 |
- send命令接收一个字符串参数,并将该参数发送到指定的进程。
- expect命令与send命令作用相反,它通常用于等待进程的输出反馈。根据进程返回的内容,再发送相应的交互命令。
- spawn命令用于启动新的进程。spawn之后的send和expect命令均与spawn打开的进程进行交互。
- interact命令使用频率相对较低,通常spawn、send和expect命令已能完成任务。但在特殊场合下,如需退出自动化并进入人工交互状态,则需使用interact。例如,使用spawn、send和expect完成FTP登录及文件下载后,若希望下载结束后仍停留在FTP命令行以便手动执行后续命令,使用interact即可实现。
Expect脚本示例与模式-动作机制
代码示例
#!/usr/bin/expect
set timeout 30
set host "101.200.241.109"
set username "root"
set password "123456"
spawn ssh $username@$host
expect "*password*" {send "$password\r"}
interact
以下是一段简单代码,演示了Expect的基本用法:
#!/usr/bin/expect:指定使用expect解释器执行该脚本;
set timeout 30:设置超时时间为30秒,默认值为10秒;
set host "101.200.241.109":设置主机变量;
spawn ssh $username@$host:spawn是进入Expect环境后可执行的内部命令。若未安装Expect或在默认Shell下执行,则无法找到该命令。其主要功能是为ssh进程添加外壳,用于传递交互指令;
expect "password":此处的expect也是Expect的内部命令,用于判断上次输出是否包含“password”字符串。若包含则立即返回;否则等待设定的30秒后返回;
send "$password\r":当匹配到对应输出结果时,发送密码至已打开的SSH进程,执行交互操作;
interact:执行完成后保持交互状态,将控制权交还给控制台,此时可进行手工操作。若缺少此句,登录完成后将直接退出,而非停留在远程终端上。
上述脚本分析涉及Expect中一个核心概念——模式-动作,即expect "password" {send "$password\r"}所表达的含义。
模式-动作机制
结合expect "password" {send "$password\r"}代码解释“模式-动作”。简言之,即匹配到特定模式时,执行对应的动作;例如匹配到password字符串,则输入密码。
示例如下:
expect {
"password" {
send "$password\r"
exp_continue
}
eof
{
send "eof"
}
}
其中exp_continue表示循环匹配。通常匹配后语句会退出,但若包含exp_continue,则可不断循环匹配并输入多条命令,从而简化写法。
Expect脚本参数传递与FTP应用
参数传递
很多时候需向脚本传递参数,以下代码展示如何在Expect中使用参数:
#!/usr/bin/expect
if {$argc < 3} {
puts "Usage:cmd <host> <username> <password>"
exit 1
}
set timeout -1
set host [lindex $argv 0]
set username [lindex $argv 1]
set password [lindex $argv 2]
spawn ssh $username@$host
expect "*password*" {send "$password\r"}
interact
在Expect中,$argc表示参数个数,参数值存放在$argv中。例如,获取第一个参数为[lindex $argv 0],依此类推。
FTP下载Expect脚本
使用yum安装Expect:
yum install expect
按如下方式编写Expect脚本:
#!/usr/bin/expect -f
set ip [lindex $argv 0] #脚本的第一个参数,远程主机的IP地址
set file [lindex $argv 1] #脚本的第二个参数,指定下载的文件名
set timeout 10 #设置超时时间10秒
spawn ftp $ip #运行ftp $ip命令
expect "Name*" #如果出现Name字符
send "anonymous \r" #则输入anoymous(匿名用户)并回车
expect "Password:*" #如果出现Password字符
send "\r" #则仅输入回车
expect "ftp>*" #如果出现ftp>字符
send "get $file\r" #则发送get $file命令
expect {
"*Failed*" { send_user " Download failed\r";send "quit\r"} #如果返回的字符串有Failed,则说明下载失败,send_user回显信息 Download failed
"*send*" { send_user " Download ok\r";send "quit\r"} #如果返回的字符串有send,则说明下载失败,send_user回显信息 Download ok
}
expect eof #结束循环匹配
赋予脚本可执行权限:chmod +x expect_ftp_auto.exp
Python pexpect模块核心组件详解
pexpect模块
pexpect可视为Linux下Expect的Python封装,通过pexpect可实现SSH、FTP、passwd、telnet等命令的自动交互。
安装命令:pip install pexpect
以下是一个简单实现SSH自动登录的示例:
import pexpect
child = pexpect.spwan('scp foo user#expample.com:.') #spwan启动scp程序
child.expect('Password:') #expect方法等待子程序产生的输出,判断是否匹配定义的字符串
#‘Password:'
child.sendline(mypassword) #匹配后则发送密码进行回应
核心组件
spawn类
spawn是pexpect的主要入口,用于启动和控制子应用程序。其构造函数如下:
class pexpect.spawn(command,args=[],timeout=30,maxread=2000,searchwindowsize=None,logfile=None,cwd=None,env=None,ignore_sighup=True)
其中,command参数可以是任意已知系统命令,例如:
child=pexpect.spawn('user/bin/ftp')
当子程序需要参数时,还可使用Python列表代替参数,如:
child = pexpect.spwan('user/bin/ssh user@example.com')
参数timeout为等待结果的超时时间,maxread为从终端控制台一次读取的最大字节数,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)
调试代码时,若希望获取pexpect的输入与输出信息以了解匹配情况,一种方式是写入日志,另一种是输出到标准输出。
写入日志的方式:
child=pexpect.spawn('some_command') fout=file('mylog.txt','w') child.logfile=fout
输出到标准输出的方法:
child=pexpect.spawn('some_command') child.logfile=sys.stdout
以下为SSH远程登录示例,登录成功后显示/home目录文件并记录输入与输出:
import pexpect
import sys
child=pexpect.spawn('ssh root@172.31.208.129')
fout=open('mylog.txt','w')
child.logfile=fout
#child.logfile=sys.stdout
child.expect('password:')
child.sendline('abc@123')
child.expect('#')
child.sendline('ls /home')
child.expect('#')
pexpect匹配方法与外部命令调用
expect方法
expect定义了子程序输出的匹配规则。
方法定义:expect(pattern,timeout=-1,searchwindowsize=-1)
参数pattern表示字符串、pexpect.EOF(指向缓冲区,无匹配项)、pexpect.TIMEOUT(匹配等待超时)、正则表达式或列表。
参数timeout指定等待匹配结果的超时时间(单位秒)。触发超时时,expect将匹配到pexpect.TIMEOUT。参数searchwindowsize为匹配缓冲字符串的位置,默认从开始位置匹配。
read相关方法
以下方法均用于向子程序发送响应命令:
send(self,s) #发送命令,不回车 sendline(self,s=' '),#发送命令,回车 sendcontrol(self.char) #发送控制字符 sendeof() #发送eof
run函数
run是使用pexpect封装的调用外部命令的函数。
from pexpect import * run('scp foo user@example.com:.',events={'(?i)password':mypassword})
pxssh类封装与FTP自动化示例
pxssh类
针对SSH会话操作进行的再次封装。
class pexpect.pxssh.pxssh(timeout=30,maxread=2000,searchwindowsize=None,logfile=None,cwd=None,env=None)
常用方法:
- login()建立SSH连接
- logout() 断开连接
- prompt()等待系统提示符,用于等待命令执行结束
import pxssh
import getpass
try:
s = pxssh.pxssh() #创建对象s
hostname = raw_input('hostname: ')
username = raw_input('username: ')
password = getpass.getpass('password: ') #接收密码输入
s.login (hostname, username, password) #建立ssh连接
s.sendline ('uptime') # 运行uptime命令
s.prompt() # 匹配系统提示符
print s.before # 打印系统体术符号出现前的命令输出
s.sendline ('ls -l')
s.prompt()
print s.before
s.sendline ('df')
s.prompt()
print s.before
s.logout()
except pxssh.ExceptionPxssh, e:
print "pxssh failed on login."
print str(e)
FTP自动操作
实现自动交互登录FTP操作。
import pexpect
import sys
child = pexpect.spawnu('ftp ftp.openbsd.org') #运行ftp命令
child.expect('(?i)name .*: ') #(?!)表示后面的字符串正则表达式忽略大小写
child.sendline('anonymous') # 输入ftp账号信息
child.expect('(?i)password') #匹配密码提示
child.sendline('pexpect@sourceforge.net')
child.expect('ftp> ')
child.sendline('bin') #启用二进制传输
child.expect('ftp> ')
child.sendline('get robots.txt')
child.expect('ftp> ')
sys.stdout.write (child.before) #输出匹配的"ftp"之前的输入与输出操作
print("Escape character is '^]'.\n")
sys.stdout.write (child.after)
sys.stdout.flush()
child.interact() # Escape character defaults to ^] #让出控制权,用户可以继续当前会话手工控制子程序,默认输入"^]"字符跳出
child.sendline('bye')
child.close()
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









