【Python】使用 termios、fcntl 模拟终端键盘输入

【Python】使用 termios、fcntl 模拟终端键盘输入

在本篇文章中,我们将逐一讲解以下内容:

  1. 如何通过 python 来模拟终端的键盘输入
  2. 如何控制 docker 中的终端
  3. 为了防止终端意外关闭,如何使用 tmux 来管理终端

一、前言

在最近的工作中我遇到一个十分棘手的问题,就是我需要在测试自动化中使用他人开发的一个【命令行工具】

命令行工具示意图
这个【命令行工具】使用方法需要我在程序开始运行后,通过键盘输入一些选项才能运行,而不是一行命令直接运行,因此无法直接使用 os 模块来让【命令行工具】直接工作,所以想让这样一个工具自动化便成了一个困扰我多日的问题

通过查阅大量的资料,发现其实可以通过 python 来控制某个终端,来模拟用户的键盘输入。现在让我们进入正文

二、用 Python 控制终端

实现方法大概分为一下几步

  1. 获取你想控制的【终端的编号】:如 ttys001
  2. 将上面的编号告诉 python,打开其对应的【文件描述符】
  3. 向【文件描述符】中写入你想用键盘输入的命令
  4. 完成后,关闭【文件描述符】

大概的图示如下,看不懂没有关系,下面有每一步的详细解释

控制示意图

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

五、总结

总结一下,通过这篇文章我们讲解了

  1. 如何通过 python 来模拟终端的键盘输入
  2. 如何控制 docker 中的终端
  3. 为了防止终端意外关闭,如何使用 tmux 来管理终端

好了以上就是文章的全部内容,感谢您的耐心阅读,谢谢🙏

欢迎各位来关注我的个人网站:https://tyyuan110.com/
我会在上面定期更新相关技术博客(前端、测开、面经等),谢谢!

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值