你用过最丑的编程语言是哪个?
高赞说 Bash 和 Vimscript 丑. 我比较懂 Bash, 就来解释一下.
一开始有个叫 Stephen R. Bourne 的英国计算机科学家在 贝尔实验室 写了个 shell, 大家就叫它 "Bourne shell". 众所周知 UNIX 也是在 贝尔实验室 诞生的, 所以理所当然的第七版 UNIX 就将 Bourne shell 作为它的默认 shell (/bin/sh
), 所以我们通常说的 UNIX sh 指的就是 Bourne shell 而不是其它什么 shell.
本来 UNIX 出于教育目的, 其源码是开放给高校和其它一些研究机构的, 然后就有人通过二次开发 UNIX 挣了钱. AT&T 当初压根不重视 UNIX 这个项目, 就是后来看到别人靠它盈利就眼红了, 一气之下决定闭源. 所谓的 闭源 包括 userland 的一切 tools and utilities 的源码, sh 就在其中.
AT&T 挑了个好时机. 它趁大家已经开始广泛使用 UNIX 的时候选择了玩版权战, 这个时候已经有成千上万的 UNIX 和 sh 的用户, sh 脚本少说也有几万份. 大家都不希望代码在一个闭源的 shell 解释器上运行, 同时又舍不得抛弃这些脚本.
1980s, IEEE 和 ISO/IEC 和 The Open Group 三方联手打算制定一套供操作系统供应商遵循的准则, 来简化跨平台软件开发的任务. 这个时候自由软件运动已经展露锋芒, GNU 项目启动小几年了, RMS 算是个叫得上名号的人物, 他也是 IEEE 委员会成员. 本来那群人打算给制定的这套接口命名为 "IEEEIX" 的, 但是 RMS 说:
"IEEEIX" 的发音听起来像是恐怖的尖叫
他决定取名为 "POSIX", 后来的结果大家都懂的.
POSIX 标准中有很大篇幅花在 Shell & Utilities 这节. 包括指定 shell 的各种行为和语法, 还有其它一些实用程序, 比如 awk
, sed
, 和 grep
(对, 它们是 POSIX 标准的一部分).
出于 historical reasons, 委员会必须得采用 Bourne shell 作为标准的 POSIX shell. 但是 Bourne shell 有两个难绷的点:
- Bourne shell 是闭源的
- 它并没有明确的语法手册
所以大家只能靠观察 sh 的行为来推测 sh 的语法. 我们不知道委员会付出了多少努力, 总之最后仍然颁布了 POSIX shell 标准. 这套标准似乎仅仅只是 sh 的子集, 但无论如何已经是当时存在的最好的一套语法描述了.
GNU 项目希望也能够有自己的 shell, 当然这个 shell 必须得严格遵循 POSIX 标准, 这是 Bash 语法槽点多的根本原因 ---- POSIX shell 标准的来源本身就很扯淡. 1987 年, RMS 给一名叫 Brian Fox 的高中缀学生布置了一项任务: 编写 GNU shell. Brian Fox 很适合这个任务, 因为他没看过 sh 的源码, 不会有版权纠纷.
实际上就算看了 sh 的源码也没什么卵用, 我至少读过三篇 Chet Ramey (现任 Bash maintainer) 撰写的论文, 每一篇都引用了 Tom Duff (Plan 9 项目成员之一, 有个叫 达夫设备 的算法就是以他命名) 在某篇论文中说的一句话:
没有人真正知道 Bourne shell 的语法是什么. 甚至查看源代码也没啥卵用.
最后 Bash 如期完工, 实现了 POSIX 标准, 也吸收了 ksh
, csh
, 和 tcsh
的特性, 成为了 GNU 工程的一部分. 因为 Linux 是 POSIX-compatible 的, GNU 也是, 大家配合得很好, 所以 Linus 最终决定将他小小的 Linux 内核塞到 RMS 大大的 GNU userland 中, 于是 Bash 就这么广泛流传开来. 一直到 2020 年前后, Bash 仍然是 macOS 的内置 shell.
所以语法难学, 倒不是 Bash 想故意刁难用户, 而是因为 sh 就是这么玩的, 恰好 sh 脚本又广为流传, POSIX 标准采纳了 sh. 除了 Bash, 还有一大票 POSIX-compatible 的 shell 都是这么个语法.