Python pexpect模块及Shell脚本Expect原理解析

木白
木白
发布时间:2026-10-14| 3人浏览过
原创

本文深入解析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()

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。