【Python】使用 termios、fcntl 模拟终端键盘输入
在本篇文章中,我们将逐一讲解以下内容:
- 如何通过 python 来模拟终端的键盘输入
- 如何控制 docker 中的终端
- 为了防止终端意外关闭,如何使用 tmux 来管理终端
一、前言
在最近的工作中我遇到一个十分棘手的问题,就是我需要在测试自动化中使用他人开发的一个【命令行工具】
这个【命令行工具】使用方法需要我在程序开始运行后,通过键盘输入一些选项才能运行,而不是一行命令直接运行,因此无法直接使用 os 模块来让【命令行工具】直接工作,所以想让这样一个工具自动化便成了一个困扰我多日的问题
通过查阅大量的资料,发现其实可以通过 python 来控制某个终端,来模拟用户的键盘输入。现在让我们进入正文
二、用 Python 控制终端
实现方法大概分为一下几步
- 获取你想控制的【终端的编号】:如 ttys001
- 将上面的编号告诉 python,打开其对应的【文件描述符】
- 向【文件描述符】中写入你想用键盘输入的命令
- 完成后,关闭【文件描述符】
大概的图示如下,看不懂没有关系,下面有每一步的详细解释
2.1 获取终端编号
首先打开一个终端,并输入 ps
,在 TTY 这一列会标志每条进程所属的【终端编号】。通常我们看 bash
或·zsh
的进程所属终端,即为当前的终端。所以如图所示,我目前的终端是 ttys001
在 Linux 系统中,每打开一个终端,便会在系统的 /dev
目录下新建一个带有【终端编号】的文件,如 /dev/ttys001
这个文件表示一个串行终端设备。
在这里我们只需要知道 /dev/ttys001
代表了当前的终端即可
2.2 在 python 中打开设备
这里直接上代码,十分好理解
# 这里我们使用 open 打开了该终端(写模式)
# 得到了终端的文件描述符 file descriptor,即 fd
fd = open('/dev/ttys001', 'w')
2.3 将命令写入终端
首先贴入完整代码,看不懂没关系,之后会逐行解释
import fcntl
import termios
terminal = "/dev/ttys001"
def terminal_exec(terminal: str, cmd: str):
"""Execute cmd in the specific terminal
Args:
terminal (str): the terminal you would like to control
cmd (str): command line, such as 'ls'
"""
with open(terminal, "w") as fd:
for c in "{}\n".format(cmd):
fcntl.ioctl(fd, termios.TIOCSTI, c)
terminal_exec(terminal, "ls")
在上面的 terminal_exec 函数中,我们首先用 with open(...) as fd
打开了终端,并获取到了文件描述符
然后这段代码👇中的 for 循环,其实可以理解为我们将命令一个字母一个字母的敲到终端中
for c in "{}\n".format(cmd):
fcntl.ioctl(fd, termios.TIOCSTI, c)
至于是如何实现 “敲键盘” 的,我们需要理解以下几个函数的意思
首先我们来看这个函数 fcntl.ioctl()
(定义链接),它允许我们使用文件描述符的形式来控制 IO
fcntl.ioctl(fd, request, args)
- 第一位参数:fd,文件描述符。
- 第二位参数:request,io控制的请求。io控制中有种类繁多的请求方法,功能也五花八门,详细内容可以参考全部请求方法
- 第三个参数:args,即io的字节或字节流等
那么为了控制终端,我们在第一个参数位置填入:终端的文件描述符 fd
为了模拟键盘输入,我们在第二个参数位置填入:termios.TIOCSTI
, TIOCSTI的是模拟输入的request
最后,我们将向输入的命令拆分成一个个的字符 char,填入最后一个参数位置。
让我们来实际运行一下看一下效果,⚠️ 注意这里需要 sudo 权限才行
成功了!我们成功使用脚本来控制不同的终端了!
三、控制 Docker 中的终端
其实我工作中实际遇到的问题更加复杂一些,那个【命令行工具】是运行在一个 docker 中的。
然后我发现,在docker 中运行 ps
所得到的【终端编号】,在docker外部的 /dev
中是无法找到的。也就是说外部脚步是无法控制 docker 内部的终端的
在查阅资料后发现,如果想要向外部暴露 docker 内的终端,我们需要在 docker 启动时,就将 /dev
挂载到 docker 的 /dev
上,即:
docker run -v /dev:/dev ......
四、使用 tmux 隐藏终端
工作中发现经常无意识的关闭终端,终端关闭后,再次启动的【终端编号】和之前的不一定一样。因此,为了防止终端被意外关闭,我们使用 tmux
工具来维护终端,让它在后台运行不容易被中断
- 新建终端
# new 的意思是新建
# -s 是 --session 的缩写,意思是新建一个叫做 auto 的终端
tmux new -s auto
进入新的 session 后,我们运行 ps
得到当前窗口的【终端编号】
然后登出目前的session:先同时按 Ctrl 和 b,松开后立刻按 d
这样终端就会在后台运行了
- 登陆已有终端
# list all session
tmux ls
# tmux attach -t <session-name>
tmux attach -t auto
五、总结
总结一下,通过这篇文章我们讲解了
- 如何通过 python 来模拟终端的键盘输入
- 如何控制 docker 中的终端
- 为了防止终端意外关闭,如何使用 tmux 来管理终端
好了以上就是文章的全部内容,感谢您的耐心阅读,谢谢🙏
欢迎各位来关注我的个人网站:https://tyyuan110.com/
我会在上面定期更新相关技术博客(前端、测开、面经等),谢谢!