A text editor as a user interface

A text editor as a user interface

将文本编辑器作为用户界面

A huge difference in effort lies between making a utility that takes a few command-line arguments versus a full-blown text-based user interface (TUI). Good user interfaces are hard work to make. And yet, sometimes I want something in between. 开发一个仅接收少量命令行参数的实用工具,与开发一个功能完备的文本用户界面(TUI)之间,工作量有着巨大的差异。优秀的界面往往需要耗费大量心血,但有时我却想要一种介于两者之间的方案。

Something I’ve been experimenting with lately is using a text editor as a user interface for my programs. Having a program launch your text editor (as specified in environment variable $EDITOR) for you is a well-known idea. Here are three existing examples right off the top of my head: 最近我一直在尝试将文本编辑器作为程序的交互界面。让程序自动调用你指定的文本编辑器(通过环境变量 $EDITOR 定义)是一个广为人知的做法。以下是我随手想到的三个现有案例:

  • crontab -e to edit the cron table

  • git commit to edit the commit message

  • visudo to edit /etc/sudoers

  • vipw to edit /etc/passwd

  • crontab -e:用于编辑 cron 任务表

  • git commit:用于编辑提交信息

  • visudo:用于编辑 /etc/sudoers

  • vipw:用于编辑 /etc/passwd

All four open temporary files and allow you to edit them to your heart’s content from the warm comfort of your favorite text editor and then take that input and use it. vipw makes a good example, since its entire purpose is to ensure your edits are okay before applying them. From the man page: “vipw edits the password file after setting the appropriate locks, and does any necessary processing after the password file is unlocked. vipw performs a number of consistency checks on the password entries, and will not allow a password file with a “mangled” entry to be installed.” 这四个命令都会打开临时文件,让你在自己最熟悉的文本编辑器中随心所欲地进行编辑,随后程序会读取并使用这些输入。vipw 是一个很好的例子,它的核心目的就是在应用更改前确保你的编辑内容是合法的。根据手册页描述:“vipw 在设置适当锁定的情况下编辑密码文件,并在解锁后进行必要的处理。vipw 会对密码条目执行多项一致性检查,如果发现条目损坏,则不允许安装该密码文件。”

You, too, can use the power of text editing to control programs of arbitrary complexity. 你也可以利用文本编辑的强大功能来控制任意复杂度的程序。

Tiny example

微型示例

As a contrived example, let’s imagine I want the ability to write some text and have it reversed with the rev utility. This three-line shell script will open a temporary file with your favorite text editor and then run its contents through rev, printing the reversed result. 作为一个人为设计的示例,假设我想编写一段文本,并使用 rev 工具将其反转。下面这个三行 Shell 脚本会用你最喜欢的编辑器打开一个临时文件,然后将文件内容通过 rev 处理并打印出反转后的结果。

FILE=$(mktemp)
$EDITOR $FILE
rev < $FILE

I told you it would be contrived. But you can see how simple the concept is. I’ve given myself the full expressive power of my text editor with just a single line. It would take me years to write my own ad-hoc TUI of comparable editing power. And unlike an ad-hoc interface, I don’t have to explain how this one works! 我说了这会是一个人为的例子,但你可以看出这个概念有多简单。我仅用一行代码就获得了文本编辑器全部的表达能力。如果我要自己编写一个具备同等编辑能力的临时 TUI,可能需要耗费数年时间。而且与那种临时界面不同,我根本不需要解释这个工具该怎么用!

Full example - controlling a complicated CLI interface

完整示例 - 控制复杂的命令行界面

Command line interfaces for tools like find, ffmpeg, and rsync can be incredibly complex. Imagine controlling them with text files with comments and explanations as verbose as you like. It’s like having your own step-by-step wizard, but with zero UI programming and no new interface to learn. 像 findffmpegrsync 这类工具的命令行界面可能极其复杂。试想一下,如果能通过带有详细注释和说明的文本文件来控制它们,那该多好。这就像拥有了属于自己的分步向导,却无需编写任何 UI 代码,也不用学习新的界面。

For this example, I’ll show how I’ve created a text interface for the popular Python video downloader, yt-dlp. This could be a shell script, but I’m practicing self-care by doing it in Ruby instead (and I think Ruby is better for “shell” scripting anyway). 在这个例子中,我将展示如何为流行的 Python 视频下载器 yt-dlp 创建一个文本界面。这本可以用 Shell 脚本实现,但我为了“善待自己”,改用了 Ruby(而且我认为 Ruby 更适合编写“Shell”脚本)。

(Code omitted for brevity, see original article) (此处省略代码,请参考原文)

This is a pretty long example, but it’s a whole application with error checking and everything and it has proven quite robust so far. (A version without error checking, etc. could be a fraction of this size.) The text file specifies a destination sub-directory, a base filename, and the page URL hosting the video. 这是一个相当长的例子,但它是一个包含错误检查等功能的完整应用程序,且目前证明非常稳健。(如果去掉错误检查等功能,代码量可以缩减到原来的几分之一。)该文本文件指定了目标子目录、基础文件名以及视频所在的页面 URL。

One of the features I think is pretty neat is that the script populates the current list of available sub-directories for downloading as a comment in the text file so I don’t have to remember them. 我觉得其中一个很棒的功能是:脚本会自动将当前可用的下载子目录列表作为注释填充到文本文件中,这样我就不必去记忆它们了。

An example: 示例如下:

# cs_lectures film howto sketchbooks watercolor
howto
fix_toilet
https://example.com/how-to/fix-a-toilet.html

This will save the video as something like /media/video/howto/fix_toilet_1777324491.mp4. The base filename is appended with a timestamp so I can grab serial videos like lecture series and keep them sequential for sorting without having to add logic to figure out which number comes next (1,2,3, etc.). 这会将视频保存为类似 /media/video/howto/fix_toilet_1777324491.mp4 的文件。基础文件名后附加了时间戳,这样我就可以抓取系列讲座等连续视频,并保持它们的顺序以便排序,而无需编写逻辑来判断下一个数字应该是多少(1, 2, 3 等)。

I do not use a randomly named temporary text file because I want to re-use my last settings (if there were any). But if the file doesn’t exist, I populate it with example defaults. If I had more parameters than this, I might be tempted to add more comments or some sort of simple .ini, .conf, or .toml style input “fields” in the text file format. Keep it simple and it can be dirt simple to read those in even without a library. 我没有使用随机命名的临时文件,因为我想复用上次的设置(如果有的话)。但如果文件不存在,我会用默认示例填充它。如果参数更多,我可能会考虑添加更多注释,或者在文本格式中加入类似 .ini、.conf 或 .toml 的简单输入“字段”。保持简单,即使不使用任何库,读取这些内容也会变得非常容易。

I’ve used a variation of this trick in at least half a dozen different utilities, some of which I use daily. The technique has really grown on me. Hopefully this gives you some ideas for similar uses. 我在至少六个不同的工具中使用了这种技巧的变体,其中一些是我每天都在用的。这种方法真的让我爱不释手。希望这能为你提供一些类似的灵感。