编程与开发 · 第 4 期

30 天从零写一个 Shell,循环到底在循环什么

REPL 循环 · build your own shell

Shell 不是命令行,是一个18 行就能写出来的 REPL 循环:读输入→解析→fork→exec→wait→循环。理解这个循环,你就懂了 Shell 的心脏——管道、信号、重定向都是挂在它上面的扩展。造过一遍,命令行就不再神秘。
⏱ 约 11 分钟 🎯 想真正懂 Shell 的人 📦 源:build-your-own-x §2

01一个反共识:Shell 是个循环,不是命令行

很多人把 Shell 等同于"那个黑窗口"。其实那个窗口是终端,Shell 是里面跑的程序——本质就是一个循环。

while true do:读 → 解析 → fork → exec → wait done

读:从输入读一行命令。解析:拆成命令+参数+管道+重定向。fork:复制一个子进程。exec:在子进程里换成要跑的程序。wait:父进程等子进程结束。然后回到读。所有 Shell 功能都是在这个循环的某一步加东西:管道在 fork/exec 之间插管道连接,重定向在 exec 前改文件描述符,信号在 wait 时处理。

Shell 是个循环,
不是命令行窗口。
灏天文库 · 编程与开发 P.12

02执行循环动画:看 ls | grep 怎么跑

下面是一个管道命令的执行过程,点"逐步执行"看 fork/exec/管道怎么按时序发生。

🔄 Shell 执行循环动画
逐步执行 ls | grep txt,看每一步。
$ ls | grep txt
← 点逐步执行

03管道:两个 fork + 一个管道

ls | grep txt 看似一条命令,Shell 实际做了:

  1. 建管道:pipe() 建一对文件描述符(读端、写端)。
  2. fork 第一个子进程(ls):把管道写端接到它的 stdout,exec 成 ls。ls 的输出进管道。
  3. fork 第二个子进程(grep):把管道读端接到它的 stdin,exec 成 grep。grep 从管道读。
  4. 父进程关掉管道两端,wait 两个子进程。

所以一条管道命令 = 两个 fork + 一个管道 + 两次 exec。理解这个,N 段管道(ls | grep | sort | head)就是 N 个 fork + N-1 个管道,串起来。管道不是魔法,是文件描述符的连接。

管道不是魔法,
是文件描述符的连接。
灏天文库 · 编程与开发 P.13

04带走这套清单

✅ 从零写 Shell 6 条可执行规则

  1. 先写 18 行 REPL:读→解析→fork→exec→wait 循环跑通,再加功能。
  2. 每步加一个真实问题:先管道,再重定向,再信号,再 history。
  3. fork+exec 分两步:fork 复制进程,exec 换程序,别混为一谈。
  4. 管道 = 文件描述符连接:N 段管道 = N 个 fork + N-1 个 pipe。
  5. 对照真 Shell:每加一块看 bash 怎么做,理解它的取舍。
  6. 不追求兼容 bash:目标是理解循环,不是替代 bash。
造过一遍 Shell,
命令行就不神秘了。
灏天文库 · 编程与开发 P.14