python3 – pexpect
此函数库一般用于自动化交互式控制台命令行
1.首先安装 pexpect
[root@192 python]# python3 -m pip install pexpect
WARNING: Running pip install with root privileges is generally not a good idea. Try `__main__.py install --user` instead.
Collecting pexpect
Downloading https://files.pythonhosted.org/packages/39/7b/88dbb785881c28a102619d46423cb853b46dbccc70d3ac362d99773a78ce/pexpect-4.8.0-py2.py3-none-any.whl (59kB)
100% |████████████████████████████████| 61kB 672kB/s
Collecting ptyprocess>=0.5 (from pexpect)
Downloading https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl
Installing collected packages: ptyprocess, pexpect
Successfully installed pexpect-4.8.0 ptyprocess-0.7.0
[root@192 python]# python3
Python 3.6.8 (default, Nov 16 2020, 16:55:22)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-44)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pexpect
>>>
2.简单应用
先看一个简单的命令
#使用spawn创建一个子进程,执行ls -la命令
child = pexpect.spawn('ls -la')
#expect用来等待特定的标志,如pexpect.EOF代表子进程已结束
child.expect(pexpect.EOF)
#输出ls -la的输出结果
print(child.before)
输出效果:
b'total 12\r\ndrwxr-xr-x 2 root root 4096 Jun 10 06:56 .\r\ndrwxr-xr-x 21 pi pi 4096 Jun 10 06:55 ..\r\n-rwxrwxrwx 1 root root 305 Jun 10 07:23 0.py\r\n'
比较乱,是一个字节序列 b’*’ ,我们可以进一步用decode(‘utf-8’)处理一下,如下:
print(child.before.decode('utf-8'))
#得到输出:
total 12
drwxr-xr-x 2 root root 4096 Jun 10 06:56 .
drwxr-xr-x 21 pi pi 4096 Jun 10 06:55 ..
-rwxrwxrwx 1 root root 334 Jun 10 07:26 0.py
3.管控执行结果,等待期望的值
上文有提到,我们可以利用pexpect()来定义一个或多个期望的值,程序会监控子程序执行进程,直到等到期望值或者子程序执行完毕再继续往下走
所以我们可以使用此特性去监控一些特定的执行结果,假设我需要监控一个文件status.txt里的内容, pass/fail两种状态,两种状态分别对应不同的处理方式,代码如下:
#首先在status.txt中记录pass
import pexpect
import sys
child = pexpect.spawn('cat status.txt')
rec=child.expect(['pass','fail'])
if rec == 0:
print("pass")
elif rec == 1:
print("fail")
sys.exit()
else:
sys.exit()
输出结果:
pass
rec结果是返回的列表内的元素索引,如这里,匹配到了pass,所以返回了0.注意这里只能匹配一次,先匹配到谁,就先输出谁。
3,timeout设定
我们可以对于命令的响应设定timeout,以免cmd未相应导致脚本一直等待,影响执行,如果不做设定,默认是30s超时
import pexpect
import sys
child = pexpect.spawn('ping 192.168.9.102')
try:
rec = child.expect(['icmp_seq=15'], timeout=10, searchwindowsize=10000)
if rec == 0:
print('success')
except pexpect.TIMEOUT:
print('timeout')
如此例子,我们使用ping命令去做一个持续长期的cmd执行,抓寻关键字icmp_seq=15,正常情况下在15s的时候才能打印到此关键字,但是我设定了timeout 10 s, 所以他会在10s时停止
但我使用了try except,并且在其超时 pexpect.TIMEOUT时,打印timeout.避免了程序直接退出,这里我们还能做其他的操作
所以程序执行结果会输出 “timeout”
这里引入了一个新的参数searchwindowsize=10000,代表缓存区的大小,单位为字节,默认为2000.当然,这个值设定太大可能会影响程序执行效率
但是它并不影响 child.before 的输出,并不是说我这里设定30字节,他就只能输出30字节了,这里的缓存区大小的意思应该类似于每满缓存区大小的时候,程式回去匹配一次看是否有满足条件
4,进阶切换shell环境交互
child.sendline() 是 pexpect.spawn 对象中的一个方法,用于向子进程发送一行命令,并自动在命令末尾添加一个换行符,我们可以利用此命令进行shell交互
child.sendline(‘cmd’),会在cmd后默认加上换行符,所以这个命令能被shell直接执行
先上代码:
import pexpect
child = pexpect.spawn('/bin/bash')
child.expect('#')
child.sendline('ls -l')
child.expect('#')
print(child.before.decode('utf-8'))
child.sendline('date')
child.expect('#')
print(child.before.decode('utf-8'))
结果:
ls -l
total 12
-rwxrwxrwx 1 root root 480 Jun 10 07:54 0.py
-rwxrwxrwx 1 root root 234 Jun 10 09:14 1.py
-rwxrwxrwx 1 root root 10 Jun 10 08:08 status.txt
root@raspberrypi:/home/pi/python3
date
Sat 10 Jun 09:14:45 BST 2023
root@raspberrypi:/home/pi/python3
以上均为打印结果,因为我们启用了一个/bin/bash的子进程,它是一个shell环境,所以我们使用child.before输出时,它将shell cmd,以及这个shell cmd执行结果都打印了出来,又因为关键字为 #,所以他输出了#号前的所有输出,没有#
结束这个子进程可以使用child.kill() 或 child.close()
使用 child.kill() 方法可以强制终止子进程,并且会立即释放子进程占用的资源
使用 child.close() 方法会正常结束子进程,并且会等待子进程的缓冲区输出全部读取完成后才返回。这样一来,你可以确保程序正常退出,并且已经读取了所有子进程输出的数据。
5.再加ssh远程?
既然这么好用,我直接应用于远程环境应该不过分吧?
上代码:
import pexpect
import sys
try:
child = pexpect.spawn('ssh root@192.168.9.102')
while True:
rec = child.expect(["yes/no","password:"])
if rec == 0:
child.sendline('yes')
else:
child.sendline('aa')
result = child.expect(['#', 'Permission denied', pexpect.TIMEOUT])
if result == 0:
print("ssh success")
break
elif result == 1:
print("password error")
sys.exit(1)
elif result == 2:
print("ssh is timeout")
sys.exit(1)
child.sendline('ls -la')
child.expect('#')
print(child.before.decode('utf-8'))
except pexpect.exceptions.EOF:
print("ssh closed")
sys.exit(1)
except pexpect.exceptions.TIMEOUT:
print("cmd timeout")
sys.exit(1)
except Exception as e:
print("error:", e)
sys.exit(1)
这里我们首先使用try-except语句来处理一些异常错误,如EOF,TIMEOUT等
这里就是使用while循环,首先检查ssh命令执行后打印为"yes/no"还是"password:“,如果是前者,则输入yes,然后while循环再来判断,如果是后者,就输入登录密码,我这里为"aa”
输入密码是有可能是错误密码的,所以这里我们多加了一个判断,判断是’#‘还是’Permission denied’,如果是"#“说明登录成功了(假如你登录的不是root用户,这个应该是”$",具体情况具体分析)
如果成功就跳出while循环,如果失败,或是直接验证密码超时,都直接停止
成功登录后,我们此时是一个shell环境,所以直接下我们需要执行的shell命令,这里我使用"ls -la"来实验,然后使用child.expect(‘#’)来等待执行结束
注意,如果你要执行的命令很久,假如是fio,需要加上timeout=xx,否则他默认30s没等到就直接超时退出了
然后下面 except就是一些一场状况的处理,共三种,EOF,TIMEOUT和其他
以上就是关于pexpect库的一些简单运用了