Asciinema

2022 04 21, Thu

记录步骤的痛苦

我最近经常要重复很多命令,来验证过程的正确性。但是有的时候做了什么没做什么经常会忘记,或者记混掉。就想找一个办法去把操作录制下来,但是呢有几个问题。

录制屏幕很费磁盘空间,也非常耗电,这让最近经常跑装修工地又充不上电的我非常尴尬。而截屏呢,全靠自觉,经常会出现做完一遍只有开头截了屏的情况。

jupyter notebook 倒是可以记录步骤,但是并不能很方便的编辑系统服务啊,记录我ssh到远程做的操作啊什么的。

这就让我非常苦恼。有没有办法记录终端内容呢?

还真有。

从电传打字机到终端

从非打印字符开始说起

我们在学习编程的开始就学过ASCII码表。但是会发现其中可打印字符其实并不多,根据一般的定义一共也就95个,除去特殊意义的0以外,ASCII字符空间中还有32个字符是不可见的。

这 32 个不可见字符加上 0 就是我们常说的控制字符。可是我们在终端中打印这些字符时,我们能发现有反应的,通常只是bell,我们可以听到电脑主机或者音响哔的一声😂。这曾经让年幼的我以为控制字符就只能控制个喇叭,直到开始用 Linux。

TTY

TTY全称是 Teletype,电传打字机。其实这个名字一开始是公司的名字,后来泛指所有的电传打字机。这类打字机的特点就是通常是两台同样的或者兼容的设备连接,互相之间发送一定的序列从而传达信息。和电报很像,只不过是打字机的形式。

Unix 的终端也叫 TTY,因为早期的终端就是非常像电传打字机,我们会在打字机上敲下命令,打字机会把输出打印到纸张上。这两天有国外的Marc搞文艺复兴,用打字机当Linux终端。

当然现在我们说 TTY 都是说一个类似于打字机的环境,都是在一个显示器里面。比如说我们如果没有启动桌面环境,启动起来就是一个 TTY 的环境。也有一些情况 TTY 会开在其他地方,比如说串口输出,这种方法在树莓派啊、或者虚拟机上非常常见,比如说 pi-zero 这种没有办法连接显示器的设备,用串口就是最优选。

TTY 通常可以有很多个,比如说屏幕上可以有多个 TTY 比如说 tty1 tty2 啊,比如说 ssh 的 session 啊,都是 TTY 。和电传打字机类似,一头呢连接屏幕,另一头连接程序,你输入的会经过 TTY 发送给程序的 stdin ,程序输出到 stdout 和 stderr ,经过 TTY 处理,发送给显示设备。

但是就像上面说的,TTY会处理的不只是普通字符,还会处理一些特殊的指令。最常见的就是CR LF,也就是我们Windows下面写程序用到的 \r\n ,初次之外一般来说都支持Backspace、Tab、响铃,诸如此类的。在一些特定的TTY中,会支持比如说闪烁、ANSI 颜色,还有我们常见的加粗下划线删除线等样式。像是我们有的时候想输出颜色,那么C语言输出 \x1B[31mTexting\033[0m\t\t 就可以看到效果。

更高级一些在Unix中,TTY还包括一个输出缓冲区,ncurses之类的库可以通过操作这个缓冲区来绘制更丰富的样式,比如说特定的前景北京。这就使得比如说 readline 啊 curses 啊这类库得以在终端中绘制丰富的图形。

单体以及CS架构的终端和 Multiplexer

虽然我们有 TTY 了,但是仍然有很多问题,比如说一个TTY只能运行一个登录程序跑一个shell一个命令,加上我们现在通常用图形界面占用系统的一个 Seat,来回切换tty是不那么方便的。所以我们有了终端以及 Multiplexer。

以前我们说 终端 ( terminal ) 一般就是说我们操作的机器部件,比如说打字机就是终端,纸带也算是终端,我们的手机也算是终端。

现在我们的电脑越来越高级,终端的含义,尤其是在开发者的语境中,也逐渐转向了特指运行命令的提示符界面。比如说 Windows 的CMD/ConHost、PowerShell、Windows Terminal,macOS 的 Terminal.app 、 iTerm.app,还有 Linux 的 Gnome Terminal、Guake、Konsole 等等。

有的终端比较简单,就像是 sakura,或者是 iTerm,他们会直接准备好 pty 啊缓冲区啊这些的,然后启动 shell 或者启动你的命令。也会有一些终端会先启动一个服务,在这个服务中准备 pty 和各种资源,通过各种方法把信息传给图形界面再显示,这样好处就是标签页可以拖着到处跑,飘来飘去的。

把 pty 连到前端

既然这些终端都可以分离成CS架构,那么我们进一步改成特殊的BS架构可以吗?当然是可以啊。这里面出名的就是两个项目,一个是 xTerm.js ,一个就是我们今天要说的 asciinema。

xTerm.js

xTerm.js 是一个开发用的 SDK,主要的目的是为了在网页里面集成一个终端,比如说 cockpit 、 jumpserver 乃至 vscode 都是使用了 xTerm.js。

开发的时候需要自己写一个后端,创建 pty 以后,一端直接作为新进程的 stdin 和 stdout/err ,另一端的读写操作直接用websocket连接到前端,就可以在前端展示一个终端。

asciinema

asciinema 是另一个BS架构的工具,不同的地方是 asciinema 把 pty 的 IO 都录制了下来,之后需要的时候再播放就好了。我们通常会用 asciinema 命令启动一个新的 shell ,然后在新的 shell 中执行命令、自动记录输出,然后还是用 asciinema 命令上传或者播放录屏。

上传以后会给你一个 URL,把 URL 分享给其他人就可以分享你的终端输出啦。

把 asciinema 嵌入自己的网页中

有的时候我们想把 asciinema 嵌入到自己的网站里面,比如说博客、或者教程,亦或是需要一种可以记录流程的形式的地方。一般来说有两种方法。

使用 asciinema 公有云的分享功能

上面提到 asciinema 可以 upload 一端录制,上传以后会给你一个URL,点开以后里面有个分享,截止写这篇文章的时候,asciinema 会直接给你几种形式的分享:

  • 分享链接,和upload给出的一样。
  • 图片链接的HTML代码。
  • 图片链接的Markdown代码。
  • 嵌入播放器的HTML代码。

使用 asciinema-player

asciinema 官方也有开源的 asciinema-player 的实现,可以直接在浏览器里面展示终端输出。就像是我这边文章一样。

当然我的模板也添加了一些额外的工具,比如说 short code:

{{ $url := index .Params "url" }}
{{ $rows := index .Params "rows" | default 12 }}
{{ $cols := index .Params "cols" | default 80 }}
{{ $speed := index .Params "speed" | default 1 }}
<asciinema-player src="{{$url}}" rows="{{$rows}}" cols="{{$cols}}" speed="{{$speed}}"></asciinema-player>