造轮子工程 · 第 2 章 造一个 Shell


文档摘要

造轮子工程 · 第 2 章 造一个 Shell 章节摘要:Shell 是本书选作开篇的第一个实战项目,原因有三。第一,它是「你能天天用」的工具——造完一个能用的小 Shell,你能立刻拿它跑自己熟悉的命令,这种「我造的东西真在工作」的反馈极其宝贵。第二,它的难度恰好「踮脚能够到」:复杂到能教会你 fork/exec、管道、信号、作业控制等系统编程核心概念,又不至于像造操作系统那样需要一年。第三,它与姊妹篇《Linux 命令》第 9 章直接衔接——当你写 Shell 脚本写到「想让这个脚本语言更好用」时,造一个 Shell 就是顺理成章的下一步。

造轮子工程 · 第 2 章 造一个 Shell

章节摘要:Shell 是本书选作开篇的第一个实战项目,原因有三。第一,它是「你能天天用」的工具——造完一个能用的小 Shell,你能立刻拿它跑自己熟悉的命令,这种「我造的东西真在工作」的反馈极其宝贵。第二,它的难度恰好「踮脚能够到」:复杂到能教会你 fork/exec、管道、信号、作业控制等系统编程核心概念,又不至于像造操作系统那样需要一年。第三,它与姊妹篇《Linux 命令》第 9 章直接衔接——当你写 Shell 脚本写到「想让这个脚本语言更好用」时,造一个 Shell 就是顺理成章的下一步。本章导读 Shell 的七个子系统:命令读取与解析、内建命令(cd/exit 等)、外部命令(fork+exec)、管道(连接多个进程的 stdin/stdout)、重定向(操作文件描述符)、作业控制(后台/前台/挂起)、脚本能力(变量/条件/循环)。我们给一个「最小 Shell 一百行」的路线图,每一步标注它依赖你从《Linux 命令》哪一章学的能力。读完本章,你能动手写一个能跑 ls | grep foo | wc -l 的小 Shell。

学习目标

阅读完本章,你应当能够:

  1. 说清 Shell 的本质:一个「读入命令行、解析、fork 出子进程执行、等它结束、回到读取」的循环(REPL)。
  2. 区分内建命令(cd/exit/export)与外部命令(ls/grep),理解为什么 cd 必须是内建的。
  3. 用 fork + exec 实现外部命令的执行,理解「fork 复制进程、exec 替换程序」两步分离的设计。
  4. 用 pipe + dup2 实现管道,理解 A | B 是「A 的 stdout 接到 B 的 stdin」。
  5. 用 dup2 实现重定向(><>>),理解它本质是「把文件描述符 0/1/2 换成某个文件的句柄」。
  6. 实现基本的作业控制(& 后台、Ctrl+Z 挂起、fg/bg),理解信号(SIGTSTP/SIGCONT)在其中扮演的角色。
  7. 给你的 Shell 加上最基础的脚本能力(变量、$?&&/||),体会「Shell 也是一种编程语言」。

核心概念速览

Shell 的全部魔法,浓缩成一个循环:读取 → 解析 → 内建就自己做、外部就 fork+exec → 等待、收退出码 → 回到读取。管道和重定向是这个循环里 fork 阶段的「文件描述符接线」技巧。

子章节导航

01 REPL 主循环与命令读取

Shell 的骨架是一个 REPL(Read-Eval-Print Loop):读取一行、执行、回到读取。讲清读取用什么(fgets/readline)、提示符怎么打印、为什么 Shell 没有「Print」这一段(命令自己负责输出)。呼应《Linux 命令》第 1 章的「提示符」概念。

02 命令解析:词法与管道识别

把一行字符串切成「命令 + 参数」,并识别其中的特殊符号(|><&;)。讲清这一步要把字符串拆成「管道段」和「重定向目标」,为后续 fork 与 dup2 做准备。呼应《Linux 命令》第 1 章的「Shell 展开」。

03 内建命令与外部命令的差别

cdexitexportalias 必须是内建的——因为它们改变的是 Shell 自己的进程状态(当前目录、环境变量),fork 出子进程改了也没用。讲清「为什么 cd 不能是外部程序」这个经典问题,这是理解内建命令存在意义的关键。

04 fork + exec:执行外部命令

Unix 进程创建的核心两步:fork(复制当前进程,得到子进程)、exec(在子进程里把程序映像替换为另一个可执行文件)。讲清为什么是两步分离(便于在 fork 之后、exec 之前做重定向接线),以及 wait 收尸。呼应《Linux 命令》第 6 章的「进程」概念。

05 管道与重定向:文件描述符的接线

管道 |pipe 系统调用建一对文件描述符,dup2 把子进程的 stdin/stdout 接到这对 fd 上。重定向 >/< 则用 dup2 把 fd 1/0 接到某个打开的文件。这是 Shell 里最巧妙的部分——理解了 dup2,管道和重定向的神秘感就消失了。

06 作业控制:后台、挂起与信号

&(后台运行)、Ctrl+Z(挂起,发 SIGTSTP)、fg/bg(恢复,发 SIGCONT)。讲清作业控制本质是「Shell 用信号管理一组子进程」,以及为什么需要进程组(process group)的概念。呼应《Linux 命令》第 6 章的「信号」。

07 给 Shell 加上脚本能力

让你的 Shell 支持变量(x=5$x)、退出码($?)、条件(&&/||)。这一步让 Shell 从「交互式命令执行器」升级为「可编程脚本语言」。呼应《Linux 命令》第 9 章——那章是「用 Shell 脚本」,这章是「造 Shell 脚本能力」。

子章节之间的逻辑关系

本章遵循「骨架 → 解析 → 执行 → 接线 → 控制 → 脚本」的递进路径,从最小可用一路扩到接近真实的 Shell:

REPL 主循环 (01) ── 先有"读-执行-循环"的骨架 │ ▼ 命令解析 (02) ── 把字符串拆成可执行的指令 │ ▼ 内建vs外部 (03) ── 区分"自己做"与"fork 出去做" │ ▼ fork+exec (04) ── 外部命令的执行机制 │ ▼ 管道重定向 (05) ── 文件描述符接线(最巧妙的部分) │ ▼ 作业控制 (06) ── 用信号管理后台与挂起 │ ▼ 脚本能力 (07) ── 从执行器升级为脚本语言 │ ▼ 第 3 章:造命令行工具(CLI),Shell 的近亲

这是一条「从最小可用到接近真实」的路线:01-02 是骨架与解析,03-04 是「能跑外部命令」的最小 Shell(约一百行即可达成),05 是最巧妙的管道/重定向,06-07 是进阶。建议你的实现分阶段:先做到 04(能跑单条命令),再做到 05(能跑管道),最后挑战 06-07。

前置知识与后续延伸

前置知识:

  • 姊妹篇《Linux 命令实战》第 1 章(标准流、管道、重定向——你要先会用,再造)
  • 姊妹篇《Linux 命令实战》第 6 章(进程、信号、文件描述符——fork/exec/信号的理论基础)
  • 姊妹篇《Linux 命令实战》第 9 章(Shell 脚本——你要先会用脚本,再造脚本能力)
  • 一门系统编程语言(C 最经典,Python/Rust/Go 亦可,本书不绑定语言)

本章为后续章节奠定的基础:

  • fork/exec/pipe/dup2 是系统编程的核心原语,第 7 章(造 Web 服务器)会再次用到
  • 「REPL + 解析 + 执行」的架构骨架,是第 5 章(造编辑器)、第 9 章(造编译器 REPL)的共通模式
  • 文件描述符与信号的理解,贯穿第 7、8 章
  • 造完 Shell,你就完成了从「会用命令」到「会造命令工具」的跨越,这是全书难度梯度的第一档

发布者: 作者: 灏天文库 转发
评论区 (0)
U