终端里到底有什么 书架

第 05 章 / 共 12 章

第五章 · shell 是另一个人

前面几章里有个角色一直躲在幕后:你一打开终端就看到 $,敲命令它执行,按 Tab 它补全,按上箭头它翻出历史。很多人管这一切叫「终端」——其实不是。终端(第三章那个画格子的程序)只负责显示和转发字节,干这些活的另有其人:shell,一个再普通不过的程序,bash、zsh、fish 都是它。

先做一个实验:shell 是可以换的

在终端里敲 bash 回车——看起来什么都没发生,提示符变了变而已。其实你已经启动了一个新 shell,套在旧 shell 里面。敲 exit 就退回原来那个。再敲 echo $SHELL,它会告诉你你的默认 shell 是谁(mac 现在默认 zsh)。终端窗口没变,里面跑的程序换了一个又一个——这就是「终端和 shell 是两个程序」最直接的证据。

分工一句话:终端管「像素和按键」,shell 管「你敲的这行字是什么意思」。终端把字节递给 shell,shell 解析、执行、把结果的字节递回去,终端负责画出来。

提示符、编辑、历史、补全——全是 shell 的私活

那个 $ 或 % 提示符,是 shell 打印的一个普通字符串(存在变量 PS1 里,你可以改成任何样子,这就是那些花里胡哨显示 git 分支的提示符的原理)。它不是什么系统功能。

更妙的是你敲命令时的编辑体验:左右移光标、Ctrl-A 跳行首、上箭头翻历史、Tab 补全。第二章说过,程序默认按行收输入——可这些功能要求按一个键立刻反应,怎么办?答案:shell 把终端切成了原始模式,自己一个键一个键地收,自己维护一个「当前这一行」的草稿,你按方向键它挪光标(用第四章的暗号),你按上箭头它擦了当前行、重写一条历史命令,你按回车它才把整行当命令执行。这套行编辑功能有个著名的实现叫 readline,bash 和很多工具都在用。

所以「命令行界面」其实是 shell 用上一章的转义序列手搓出来的一个迷你编辑器。你感觉像系统功能的一切——提示符、历史、补全——都只是一个程序在勤勤恳恳地发暗号。

shell 的本职:翻译官与包工头

编辑完一行、你按下回车,shell 的真正工作才开始。它干的事里有两件特别值得知道,因为它们解释了大量「命令行玄学」:

第一件:通配符是 shell 展开的,程序根本没见过。你敲 rm *.log,shell 先去目录里查哪些文件匹配 *.log,把名单列出来,然后把「rm a.log b.log c.log」交给 rm 执行。rm 从头到尾不知道有通配符这回事。推论:如果一个目录下没有匹配的文件,有些 shell 会把 *.log 原样传给程序,程序报「找不到叫 *.log 的文件」——错误信息里那个星号就是这么来的。变量也一样:echo $HOME 里的 $HOME 是 shell 替换掉的,echo 只收到了替换后的结果。

第二件:启动程序 = 分身 + 变身。执行 ls 时,shell 先用 fork 克隆一个自己(分身),分身再用 exec 变身成 ls(包工头把手里的活整个换掉),变身后的程序继承 shell 准备好的 stdin/stdout,跑完退出,shell 再打印下一个提示符。你在命令行敲的每个外部命令,都是这样「克隆—变身—等死—收尸」的一轮。

为什么 cd 必须是「内建命令」

用上面的模型能推出一个漂亮的结论:cd 不可能是一个外部程序。因为「当前目录」是每个进程自己的属性——如果 cd 是外部程序,shell 克隆分身、分身变身成 cd、cd 改了自己的目录然后退出……shell 的目录纹丝不动,命令等于白敲。所以 cd 必须是 shell 自己动手改自己状态的「内建命令」(export、alias 同理)。敲 type cd 和 type ls 对比一下,前者是 "shell builtin",后者是 /bin/ls。

配置文件:shell 的启动仪式

每次打开终端,shell 启动时会先跑一个脚本:zsh 读 ~/.zshrc,bash 读 ~/.bashrc。你的 PATH、别名、提示符样式全在里面。「改了配置不生效」的答案是「shell 只在启动时读一遍,source ~/.zshrc 让它重读」;「为什么我的别名在脚本里没用」的答案是「脚本是非交互 shell,默认不读那个文件」——都是同一个模型的推论。

一句话总结:shell 是个可以被替换的普通程序;你以为是「终端」的智能(提示符、编辑、历史、补全)全是它用原始模式和转义序列手搓的;它的本职是把一行字翻译成「展开通配符和变量 → 克隆分身 → 变身执行」。

shell 启动程序时「准备好 stdin/stdout」这句话,前面一直轻轻带过。这两根管子其实能被随意改接——接文件、接另一个程序——这就是 Unix 最厉害的玩具:管道与重定向。下一章见。

终端里到底有什么
费曼式写法 · 由多个 AI 代理撰写与互相审校