CW Logo
Back to posts

CLI 和 TUI:同一个终端,两种交互方式

Developer ToolsAI-written

打开终端,输入 git status,屏幕会列出仓库状态,然后继续等你输入。换成启动 Lazygit,终端里会出现文件列表、分支和改动预览,你可以移动选中项,在几个面板之间切换。

它们都在终端里工作,但你跟它们打交道的方式不同。前者主要通过命令表达意图,后者把可操作的内容组织成一个持续存在的界面。这就是理解 CLI 和 TUI 最直接的入口。

CLI 与 TUI 的交互对照:左侧输入 git status --short 后得到文件状态;右侧在文件列表中选中 README.md,并在相邻面板查看改动。
同样是查看文件改动,CLI 以命令和输出为主,TUI 把选择和预览留在屏幕上。百炼生成的交互示意图,并非实际软件截图。

CLI:把要做的事写成命令

CLI 是 Command-Line Interface,中文通常叫命令行界面。你按程序约定的语法输入命令、选项和参数,程序据此执行任务,返回结果。

例如,已经安装 Git,并且当前目录位于一个 Git 仓库中时,可以输入:

bash
git status --short

这里的 git 是程序名,status 是子命令,--short 是要求使用简短输出格式的选项。并不是所有 CLI 都采用这套结构,不过“程序名后面跟选项和参数”很常见。

假设 README.md 有尚未暂存的修改,另有一个未跟踪的 notes.md,输出可能是这样:

text
 M README.md
?? notes.md

这段是示例输出,不是当前博客仓库的运行记录。第一行开头有一个空格:Git 的简短状态格式使用两列状态字符,位置也有含义。第二行的 ?? 表示文件尚未被 Git 跟踪。示意图用于说明交互方式,具体格式以代码块和 Git 文档为准。

接下来想看 README.md 改了什么,可以继续输入:

bash
git diff -- README.md

这里的 -- 用来分隔选项与文件路径。这条命令查看该文件工作区中尚未暂存的差异,不会替你提交修改。你把问题写清楚,程序给出对应结果;要问另一个问题,再执行另一条命令。

CLI 的价值也不只在于少点几次鼠标。命令可以被记录、重复执行,并组合进更大的流程。 一段排错说明可以直接给出命令,构建系统可以按顺序运行程序,脚本可以检查结果后决定下一步。

代价是你需要知道命令叫什么、选项是什么意思。终端不会因为一条命令写得简短,就自动判断它是否符合你的意图。尤其涉及删除、覆盖和远程发布时,读懂命令比照着输入更重要。

TUI:在终端里操作一个界面

TUI 通常展开为 Text User Interface,也常见 Terminal User Interface 的说法。中文可以理解为文本用户界面或终端用户界面。本文用它指终端中以列表、面板、菜单、输入框等方式组织交互的程序。

在这种界面里,屏幕不只是往下追加输出。程序会维护当前选中项、光标位置、打开的面板,以及你正在编辑的内容,再根据操作刷新显示。你可以先浏览文件列表,选中某个文件,看旁边的差异,然后决定是否继续处理。

Lazygit 就是一个 Git TUI。它把仓库里的文件、分支、提交等内容放进不同面板,让人可以边看边操作。图中的右半部分借用了“文件列表加差异预览”这个思路,但没有复刻 Lazygit 的真实布局,也不能把图里的按键当作它的快捷键说明。

TUI 往往提供方向键、Tab 或其他快捷键操作,有些也支持鼠标。比如 Textual 的输入文档 同时讨论键盘和鼠标事件。因此,“只能敲键盘”不是区分 TUI 的必要条件,是否占满整个终端也不是。

相较于记住每一步命令,TUI 把一部分选择直接摆在眼前,适合需要连续浏览和判断的工作。不过它仍然有自己的学习成本:当前焦点在哪个面板、同一个键在不同位置做什么、怎样确认或取消操作,都需要理解。界面里能按出来的操作,也不一定适合交给脚本重放。

终端和 Shell 又是什么?

这几个词经常一起出现,容易被当成同一件东西。

日常桌面上说的“终端”,通常是终端模拟器,例如 Windows Terminal、macOS 的终端应用。它负责显示程序输出,并把键盘等输入交给正在运行的程序。窗口的字体、配色和标签页属于这一层。

Shell 则是解释和执行命令的程序,Bash、Zsh、PowerShell 都是例子。你在提示符后输入 git status,Shell 解析这行输入,找到 Git 并启动它。命令结束后,通常又回到 Shell 的提示符。

终端窗口承载 Shell 和应用;Bash 或 PowerShell 可以启动以 git status 为例的 CLI 应用,也可以启动以 lazygit 为例的 TUI 应用。两个应用分支平级。
终端负责承载显示与输入,Shell 可以启动不同类型的应用。箭头表示常见启动关系,不表示所有输入输出都必须经过 Shell。百炼生成示意图。

如果启动的是一个 TUI,程序会在运行期间处理自己的界面交互。你按方向键移动选中项,并不意味着 Shell 每次都在执行一条新的命令。退出程序后,通常才重新由 Shell 接受命令。

Shell 自身也提供 CLI,所以这些概念不是互斥的分类。终端还可以直接启动某个程序,并非每次都必须先经过一个交互式 Shell。图里画的是常见使用方式,不是完整的进程或输入输出架构。

还可以顺便澄清 GUI:它是 Graphical User Interface,即图形用户界面。终端模拟器本身可以是一个有窗口、按钮和设置页的 GUI 应用,而窗口内部运行的程序又提供 CLI 或 TUI。两者并不矛盾。Microsoft 的终端 FAQ 对终端与 Shell 的职责也有专门说明。

怎么选,取决于这一步要做什么

如果你已经知道要查哪个文件,只需要得到一次结果,直接运行命令通常很顺手。如果还不知道问题在哪,需要不断切换文件、对照改动,TUI 的持续预览会更有帮助。同一次工作中交替使用它们很正常,不必为整个项目只选一种界面。

把任务交给脚本时,需要再多看一层:有 CLI 不等于适合自动化,输出是文本也不等于格式稳定。 程序可能弹出确认问题,可能调用分页器,也可能把颜色控制字符混进输出。CLI 也可以是交互式的;反过来,一个以 TUI 著称的工具也可能另外提供非交互模式。

还是以 Git 为例。人眼看状态可以使用 --short,脚本读取状态则更适合它专门提供的稳定格式:

bash
git status --porcelain=v1

Git 官方文档 说明,porcelain v1 格式不会因 Git 版本或用户配置而发生不兼容的改变。涉及空格、换行等特殊文件名时,还应考虑 -z 的 NUL 分隔输出,并让下游程序按这种格式解析,而不是简单按空格切字段。

退出状态同样要按具体命令理解。git status 正常执行成功,不代表仓库没有改动;要判断工作区是否干净,需要检查状态结果。自动化需要的是清楚的输入、输出和失败约定,不只是一个能在终端里运行的程序。

对于无人值守任务,一般优先选择有明确非交互模式、机器可读输出和退出状态约定的接口。对于有人坐在屏幕前连续浏览、选择和修改的任务,TUI 可以减少在输出结果与下一条命令之间来回切换的负担。

下次看到一个终端工具,可以先看你究竟在操作什么:是在写一条指令,还是在移动焦点、选择对象、浏览一个持续更新的界面。这个区别,比窗口是不是黑底、有没有彩色文字,更能说明 CLI 和 TUI 各自在做什么。