相对有名的可用于自动化应用程序交互的模块,可用于 ssh、ftp 等程序。
环境:Ubuntu 20.04 LTS
最近想做一个游戏服务器管理器,以 Python 为主语言,对 Linux 上的常驻程序进行管理。需要满足:
- 无阻塞启动程序
- 实时读取程序输出
- 实时进行程序输入
- 定时进行程序重启
等功能要求。
要求不是很多,也不是很复杂,但 Pexpect 完成得不是很好。
开始
1 | child = pexpect.spawn('ssh uesr@IP') # 执行命令 |
spawn()
1 | pexpect.spawn(command, args=[], timeout=30, maxread=2000, |
command
1 | # 推荐直接使用命令字符串 |
timeout
默认 30s,超时则报错。
logfile
wiki:
日志文件成员打开或关闭日志记录。所有输入和输出都将复制到给定的文件对象。将 logfile 设置为 None 以停止记录。这是默认设置。将日志文件设置为 sys.stdout 以将所有内容回显到标准输出。每次写入后都会刷新日志文件。
1 | 'ping 127.0.0.1', logfile=sys.stdout, encoding='utf-8') child = pexpect.spawn( |
只能打印输入,无法打印输出,显然,wiki 有误。
cwd
程序运行环境。需要注意的是,command
寻址仍然是从当前路径开始寻址的,并非 cwd
所指代的路径。
expect()
1 | expect(pattern, timeout=-1, searchwindowsize=-1, async_=False, **kw) |
pattern
正则表达式,但并不是非常正则,个人认为该模块最大的坑。
$
行尾匹配的模式是无用的- 始终进行非贪婪匹配
1 | # 以下将始终只返回一个字符 |
以上是 wiki 里说的,但实际上 '.+'
并不是只返回一个字符:
1 | 'ping 127.0.0.1', encoding='utf-8') child = pexpect.spawn( |
如上,'.+'
直接匹配到了行尾,显然,wiki 有误。
但,这样岂不是更好?NO!
1 | ... |
1 | on 0: stdout: |
如上,是我个人开发中一段代码。readline 有输出,表示子程序其实是正常输出了内容的,但 '.+'
什么都没有匹配到。但这同样表明 wiki 有误,因为 wiki 可是说会匹配一个字符的。
Github 上有个 issue,那位仁兄的测试代码是这样的。
1 | import pexpect |
1 | $ python3 test.py |
他的结论是「'.+'
会匹配到行尾」,显然,他的结论是错的。只是因为 ping 输出是一行行的,而 expect 又太快,所以导致每次匹配的都是刚好一行。但无论他的结论如何,'.+'
都有问题,只需静候作者处理 issue 即可。
但,距离该模块上次更新已经 15 个月了。
timeout
timeout=-1
:默认超时时间timeout=None
:无限期阻塞
before & after
send(s)
发送字符串 s
到子程序。用的不是很多。
sendline(s)
发送字符串 s + '\n'
到子程序。一般用这个。
sendcontrol(s)
发送控制字符到子程序,如 Ctrl + C、Ctrl + D。
1 | child.sendcontrol('c') # 一般用于让子程序自动退出 |
read(size=-1)
读取 size 大小的字节,如果 size=-1
则读取直到达到 EOF。如果子程序运行结束,那么当然可以 read 成功,非常完美;但如果子程序会一直运行,一直输出,那么 read 会导致无限期阻塞。
readline()
读取一行,如果遇到 EOF 则返回空字符串。同样,子程序运行,但不输出,则 readline 也会导致阻塞。
interact()
进入用户交互模式,效果同直接运行子程序,能够持续看到程序输出,进行手动输入。按 Ctrl + ]
退出该模式。
isalive()
子进程运行则返回 True,否则返回 False。
wait()
阻塞,等待子进程运行结束。
问题
对于开头说的开发的四个需求,并不知道怎么实现「实时读取程序输出」。expect 太怪了,read、readline 都是阻塞的,stdout=file 也不太行,压根儿就不写文件。
模块做得不错,但可惜貌似没在更新了。如果在下能读懂其源码,亲自更新修复,那也是相当不错的,可惜尝试过了,并不是很能读懂。
但后又仔细研究了一番 Python 官方提供的 subprocess,惊然发现它其实能满足我所有需求,不过太晚了,还是明天再写吧。
记于 2021年7月27日 1:16,工作后第三周周一。