Python Pexpect模块详解:从原理到SSH自动化实战
本文深入解析Python Pexpect模块,介绍其基于Expect原理的工作机制。详细讲解spawn、expect、send/sendline及interact等核心API的使用方法,包括参数匹配、异常处理及正则表达式应用。通过完整的SSH远程连接示例,展示如何利用Pexpect实现命令行程序的自动化交互与控制,适用于运维自动化与测试场景。
Pexpect简介
在深入探讨Pexpect之前,有必要先了解其前身Expect脚本语言。Expect由TCL语言开发,专为自动化人机交互式对话而设计,常用于处理ssh、ftp、telnet等需要命令行交互的程序。Pexpect则是Python语言对Expect功能的实现,允许开发者在Python环境中执行原本由Expect完成的任务。
Pexpect的基本工作流程主要包含三个核心步骤:
- 首先,调用spawn函数启动目标程序;
- 接着,使用expect方法等待目标程序在标准输出中打印的特定关键字;
- 最后,一旦检测到指定关键字,通过send或sendline方法向该程序发送字符串指令。
通常情况下,第一步仅需执行一次,而第二步和第三步会循环执行以完成整个任务流程。此外,Pexpect提供了丰富的其他方法,开发者可根据具体需求灵活选用。
Pexpect API
spawn类
class spawn(SpawnBase):
'''This is the main class interface for Pexpect. Use this class to start
and control child applications. '''
# This is purely informational now - changing it has no effect
use_native_pty_fork = use_native_pty_fork
def __init__(self, command, args=[], timeout=30, maxread=2000,
searchwindowsize=None, logfile=None, cwd=None, env=None,
ignore_sighup=False, echo=True, preexec_fn=None,
encoding=None, codec_errors='strict', dimensions=None,
use_poll=False):
spawn()方法用于启动一个外部程序,并返回该程序的操作句柄。获取句柄后,即可通过该对象与子程序进行交互操作。
# 子程序退出时会引发pexpect.EOF异常,即如果捕捉到pexpect.EOF则说明子程序已退出
process = pexpect.spawn('ls -l')
process.expect(pexpect.EOF)
result = process.before.decode()
print(result)
需要注意的是,command参数不支持管道符、通配符或重定向符等特殊字符含义。在Linux系统中,若需使用这些符号的特殊功能,必须通过shell参数来运行程序。
# 示例一
process = pexpect.spawn('bash -c "ls -l | wc -l"')
process.expect(pexpect.EOF)
# 示例二
# 第一个参数为主程序,而args列表里的元素是主程序的参数
process = pexpect.spawn('bash', ['-c', 'ls -l | wc -l'])
process.expect(pexpect.EOF)
expect()方法
在通过spawn()启动程序并获得控制句柄后,即可使用expect()方法等待预设的关键字。关键字可以是字符串、正则表达式、EOF(文件结束)、TIMEOUT(超时)或上述类型的列表,用于匹配子程序的输出结果。若仅传入字符串等非列表类型,匹配成功返回0;若传入列表,则返回匹配成功的列表元素索引;若匹配失败,则会抛出异常。
process = pexpect.spawn('ls -l')
# 匹配expect字符
process.expect('expect')
当expect()成功匹配关键字后,系统会自动赋值给before、after和match三个变量,利用它们可以获取子程序的运行输出详情。
- before:保存匹配到关键字之前,缓存中已有的所有数据。例如,若缓存了100个字符后匹配到关键字,before即为这100个字符(不含关键字本身)。
- after:保存匹配到的关键字字符串。
- match:保存匹配到的正则表达式对象实例。与after不同,after存储的是匹配到的具体字符串,而match存储的是正则表达式的匹配对象。
process = pexpect.spawn('ls -l')
process.expect('expect')
print(process.before.decode())
print(process.after.decode())
print(process.match)
若expect()执行过程中发生错误,before将保存截至错误发生前缓存中的所有数据,而after和match均为None。
若未匹配成功,expect()会抛出异常。开发者可以通过捕获该异常来防止错误信息在终端直接显示,从而优化用户体验。
process = pexpect.spawn('ls -l')
# 返回0表示匹配成功,返回1和2表示匹配到了异常
index = process.expect(['expect', pexpect.EOF, pexpect.TIMEOUT])
print(index)
send()/sendline()方法
sendline()与send()的主要区别在于,sendline()发送的字符串会自动附加回车符(newline)。
process = pexpect.spawn('nslookup')
process.expect('>')
process.sendline('www.baidu.com')
process.expect('>')
print(process.before.decode())
process.sendline('exit')
Pexpect还提供了众多其他辅助方法,此处不再赘述,详细用法建议参考官方文档。
interact()方法
interact()方法用于将终端控制权交还给用户(即把标准输入权交给用户)。默认情况下,Pexpect会接管所有的输入和输出;若需让用户介入完成部分交互工作,interact()便派上了用场。
# 让出控制权给用户
process.interact()
# 通过设置escape_character的值定义返回码,当用户输入此值后,会将控制权重新交给pexpect
process.interact(escape_character='\x1d', input_filter=None, output_filter=None)
应用示例
接下来,通过一个SSH连接远程服务器的示例,来实际体验Pexpect的使用方法。
#!/usr/bin/env python3.6
#-*- coding:utf-8 -*-
import pexpect
def main(server):
command = 'ssh -p %s %s@%s' % (server['port'], server['username'], server['hostname'])
process = pexpect.spawn(command, timeout=30)
print(f'命令: {command}')
expect_list = [
'yes/no',
'password:',
pexpect.EOF,
pexpect.TIMEOUT,
]
index = process.expect(expect_list)
print(f'匹配到: {index} => {expect_list[index]}')
if index == 0:
process.sendline("yes")
expect_list = [
'password:',
pexpect.EOF,
pexpect.TIMEOUT,
]
index = process.expect(expect_list)
print(f'匹配到: {index} => {expect_list[index]}')
if index == 0:
process.sendline(server['password'])
process.interact()
else:
print('EOF or TIMEOUT')
elif index == 1:
process.sendline(server['password'])
process.interact()
else:
print('EOF or TIMEOUT')
if __name__ == '__main__':
server = {
'hostname': '192.168.1.100',
'port': '22',
'username': 'admin',
'password': 'ABuklhsfnVyxI',
}
main(server)
以上就是Python Pexpect模块的使用详解,更多关于该模块的资料请关注本站其他相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













