FreeBSD 中文社区 2025 第二季度问卷调查
FreeBSD 中文社区(CFC)
VitePress 镜像站QQ 群 787969044视频教程Ⅰ视频教程Ⅱ
  • FreeBSD 从入门到追忆
  • 中文期刊
  • 状态报告
  • 发行说明
  • 手册
  • 网络文章集锦
  • 笔记本支持报告
  • Port 开发者手册
  • 架构手册
  • 开发者手册
  • 中文 man 手册
  • 文章与书籍
  • UNIX 四分之一世纪
  • Unix 痛恨者手册
  • Unix 痛恨者手册中文版
  • 前言
  • 序言
  • 事情还没到最糟,接下来只会更糟
    • 我们是谁
    • Unix 痛恨者手册往事
    • 贡献者与致谢
    • 排版惯例
    • Unix 痛恨者手册免责声明
  • 反序言(作者: Dennis Ritchie)
  • 第一部分:用户友好?
    • Unix:世界上第一款计算机病毒
    • 欢迎,新用户!就像装满六发子弹的俄罗斯轮盘赌
    • 文档?什么文档?
    • 邮件:别跟我说话,我不是打字机
    • 无聊的网络:我发帖,故我在
    • 终端错乱:靠!又挂了!
    • X-Windows 灾难:教你把 50 MIPS 工作站慢成 4.77MHz IBM PC
  • 第二部分:程序员的系统?
    • csh、管道和 find:强力工具,大力出奇迹
    • 编程:别动,这一点儿也不疼
    • C++ 九十年代的 COBOL
  • 第三部分:系统管理员的噩梦
    • 系统管理:Unix 的隐形成本
    • 安全:哦,抱歉,先生,请继续,我没意识到您是 root 用户
    • 文件系统:它确实会损坏你的文件,但你看看它有多快!
    • NFS:噩梦文件系统(Nightmare File System)
  • 第四部分:等等
    • 尾声:通过 Unix 获得的启示
    • 作者坦言 C 和 Unix 是骗局。新闻稿:立即发布
    • “宁拙勿巧”的崛起(作者:Richard P. Gabrie)
    • 参考文献:正当你以为已经脱离困境时
  • 附录
    • Unix 的流行病学(Philip E. Agre 于 1994)
    • 评论《Unix 痛恨者手册》(Andrew Kuchling 于 1997)
    • 重新审视《Unix 痛恨者手册》(Raymond, Eric S 于 2008)
由 GitBook 提供支持
LogoLogo

FreeBSD 中文社区(CFC) 2025

在本页
  • 晦涩难懂的命令名称
  • 意外总会发生
  • “rm”是永远的灾难
  • 无法通过改变 rm 的行为来解决问题
  • 始终如一的不如一
  • 令人头痛的文件名
  • 逗乐你的朋友!让你的敌人抓狂
  • 在线文档
  • 错误消息和错误检查,压根没有
  • 要删除你的文件,试试编译器吧
  • 报错的笑话
  • Unix 的态度
在GitHub上编辑
导出为 PDF
  1. 第一部分:用户友好?

欢迎,新用户!就像装满六发子弹的俄罗斯轮盘赌

上一页Unix:世界上第一款计算机病毒下一页文档?什么文档?

最后更新于4天前

有一辆 Ken Thompson 参与设计的汽车。与大多数汽车不同,这辆车没有速度表、油量表,也没有那些困扰现代司机的各种愚蠢警示灯。而如果司机犯错,仪表盘中央会亮起一个巨大的“?”问号。Thompson 说:“有经验的司机通常会知道出了什么问题。”

——佚名

计算机系统的新用户(甚至是经验丰富的用户)都需要系统给予一定程度的友好支持。至少,友好的计算机系统应为其用户提供以下便利:

  • 符合功能逻辑的命令名称

  • 对危险命令的谨慎处理

  • 命令行为及其选项和参数解释上的一致性和可预测性

  • 容易查找且易读的在线文档

  • 命令失败时提供可理解且有用的反馈

Unix 在开发期间,并未接待任何访客。每位来访者都是戴着安全帽的承包商,被指派去完成营房中尚未完成的部分。不幸的是,不仅从未邀请过工程师参与设计,甚至从未预见和规划过他们的需求。因此,许多标准设施,比如抽水马桶、中央供暖和可开启的窗户,现在都极其难以且昂贵地进行改装。尽管如此,建造者们仍对它的设计赞叹不已,以至于他们甘愿躺在没有烟雾探测器的房间地板上睡觉。

在大部分历史中,Unix 是大学和工业研究人员的研究平台。随着廉价工作站的爆炸性增长,Unix 进入了新时代——交付平台时代。这个变化很容易确定时间点:当工作站厂商为了降低非开发者的价格,将 C 编译器从标准软件套件中拆分出来时。这一变化的界限在化石记录中略显模糊,但大多发生在 1990 年左右。因此,只有在过去几年中,厂商才真正开始关心终端用户的需求和期望,而非程序员。这也解释了为什么如今公司们试图编写图形用户界面来“替代”shell 的需求。我们并不羡慕这些公司的任务。

晦涩难懂的命令名称

Unix 新用户总是对 Unix 所选择的命令名称感到惊讶。无论在 DOS 还是 Mac 上接受多少培训,都无法为你准备好去欣赏那些神秘而简短的两个字母命令,比如 cp、rm 和 ls 的“壮丽之美”。

我们这些使用过七十年代初期输入/输出设备的人怀疑,这种简短命令的退化,源于当时常用的 ASR-33 电传打字机的速度、可靠性,尤其是键盘的限制。与现代键盘不同,现代键盘按键行程的设计基于反馈原理,所需力量仅是关闭微动开关的力,而当时电传打字机的按键(据记忆)需要行程超过半英寸,而且需要施加类似骑自行车发电机的力量。用这些“怪兽”触摸打字,甚至可能撞破你的指关节。

如果 Dennis 和 Ken 用的是 Selectric 打字机而不是电传打字机,我们现在可能会打“copy”和“remove”而不是“cp”和“rm”。这再次证明,技术既限制了我们的选择,亦扩展了我们的可能。

经历了二十多年,继续沿用这种传统的理由是什么?历史的不可抗拒力量,也就是现有的代码和书籍。如果某个厂商把 rm 替换成“remove”,那么所有介绍 Unix 的书籍将不再适用,该厂商系统上的所有调用 rm 的 shell 脚本也将失效。这样的厂商还不如干脆停止执行 POSIX 标准。

一个世纪以前,快速的打字员被键盘卡住,于是工程师设计了 QWERTY 键盘来减慢他们的速度。计算机键盘不会卡键,但我们至今仍在使用 QWERTY 键盘。再过一百年,世界仍然会继续使用 rm 命令。

意外总会发生

用户非常关心他们的文件和数据。他们使用计算机来生成、分析和存储重要信息,信任计算机能够保护这些宝贵的资料。没有这种信任,用户与系统的关系就会变得紧张。Unix 滥用我们的信任,固执地拒绝保护用户免受危险命令的伤害。特别是 rm 这个最危险的命令,其存在的唯一理由就是删除文件。

所有 Unix 新手都曾“意外”且不可恢复地删除过重要文件。即使是专家和系统管理员也会“意外”删除文件。因丢失时间、努力和文件恢复而产生的费用可能每年达数百万美元。这本应是个值得解决的问题;我们不明白为什么 Unix 用户对此视而不见。难道真的喜欢祸不单行吗?

文件在 Unix 下比在任何其他操作系统中更频繁地“死亡”并需要“重生”,原因如下:

  1. Unix 文件系统没有版本号。自动文件版本控制,会给文件的新版本赋予新名字或编号后缀,从而保留文件的旧版本,避免新版本覆盖旧版本。而在 Unix 中覆盖经常发生。

  2. Unix 程序员对错误报告和检查的态度极其宽松。许多程序根本不检查输出文件的所有字节是否能成功写入磁盘,有些甚至不确认输出文件是否创建成功。但是这些程序在完成后肯定会删除输入文件。

  3. 是 Unix shell(命令行解释器),而不是客户端程序,来扩展通配符“”。shell 扩展“”导致客户端程序(如 rm)无法进行合理检查以防止灾难。即使是 DOS 也会验证像 del *.* 这样危险的命令,但在 Unix 中,删除文件的程序无法区分用户是输入了:

% rm *

还是

% rm 文件1 文件2 文件3 ...

如果能保存并传递原始命令行给被调用的客户端命令,这种情况会有所改善。也许可以利用环境变量来实现。

  1. 文件删除是永久性的。Unix 没有“undelete”(撤销删除)命令。其他更安全的操作系统中,删除文件会将文件所占用的磁盘块标记为“可用”,并将目录项移动到一个专门的“已删除文件”目录里。仅当磁盘空间不足时,才会回收这些已删除文件占用的空间。

大多数操作系统采用这种“删除 - 清空”的两步机制来将文件占用的磁盘块归还给系统。这并不是高深的技术,甚至早在 1984 年,Macintosh 就将“扔进垃圾桶”和“清空垃圾桶”区分开了。Tenex 在 1974 年就已经实现了这一点。

DOS 和 Windows 给你的东西更像是带有存水弯的污水管道,而不是垃圾桶。它们只是简单地删除文件,但如果你想“伸手”把文件找回来,至少还有一些能购买的工具来完成这项工作。这些工具确实有效——不过只是有时有效。

这四个问题相互纠缠,导致了无谓但可预见且每天都会发生的文件删除。比 Unix 出现之前,人们已经理解并广泛使用了更好的技术。而如今,随着 Unix 被接受为世界上的“标准”操作系统,这些技术正逐渐被遗忘。

欢迎来到未来。

“rm”是永远的灾难

上述原则结合起来,形成了现实生活中的恐怖故事。Usenet 新闻组 alt.folklore.computers 上的一系列交流说明了我们的观点:

日期:1990 年 1 月 10 日 星期三

主题:rm *

新闻组:alt.folklore.

有没有人曾打算输入:

% rm *.o

却不小心输入成了:

% rm *>o

现在你得到一个新的空文件叫做“o”,但它占用了大量空间!

实际上,你甚至可能不会得到名为“o”的文件,因为 shell 文档并没有明确说明输出文件“o”是在通配符展开之前还是之后创建的。shell 虽然是编程语言,但它不是很精确。

日期:1990 年 1 月 10 日 星期三 15:51 CST

主题:回复:rm *

新闻组:alt.folklore.computers

我也经历过类似的 rm 灾难。有一次,我要从磁盘上删除一个文件系统,类似于 /usr/foo/bin。我当时在 /usr/foo 目录下,已经通过以下命令删除了系统的几个部分:

% rm -r ./etc
% rm -r ./adm

……诸如此类。但当轮到删除 ./bin 时,我漏打了那个点。系统对此反应很糟糕。

Unix 并没有设计成能在失去其 /bin 目录这种致命打击后继续存活的智能操作系统。一个智能的操作系统本该给用户一个恢复的机会(或者至少确认用户是否真的想让操作系统变得无法使用)。

Unix 爱好者把偶尔的文件删除当作正常现象。例如,看看 comp.unix.questions 中的以下摘录:

6)如何“恢复删除”的文件?

总有一天,你会不小心输入类似这样的命令:

% rm * .foo

结果你实际上删除了 * 而不是 *.foo。把这看作是一个成长的仪式。

当然,所有体面的系统管理员都应该定期做备份。联系你的系统管理员,看看是否有最近的备份文件可用。

“成长的仪式”?在其他任何行业,制造商都不可能对有缺陷的产品持如此轻率的态度。“但法官大人,爆炸的油箱不过是成长的仪式。”“各位陪审团成员,我们将证明因安全装置失效导致的损害……”

“电锯对它的用户来说也只是一条成长的必经之路。”“法庭阁下,我们将证明,被基廷先生骗走毕生积蓄的退休人员,也只是经历了一条成长的必经之路。”没错。

无法通过改变 rm 的行为来解决问题

被 rm 咬了几次之后,人们往往会冲动地给 rm 命令设置别名,使其变成 rm -i,或者更好的是,用别的程序替换 rm 命令,将要删除的文件移动到一个特殊的隐藏目录,比如 ~/.deleted。这些做法会让无辜的用户产生一种虚假的安全感。

日期:1990 年 4 月 16 日星期一 18:46:33

收件人:Unix 痛恨者

主题:删除

在我们的系统上,rm 并不删除文件,而是以某种晦涩的方式重命名文件,以便所谓的“undelete”(不是 unrm)可以恢复它。

这让我在删除文件时有些不小心,因为我总以为自己可以随时恢复它们。其实不然。Emacs 中的删除文件命令并不是这样工作的,Dired 中的 D 命令也不是如此。原因当然是因为撤销删除的协议并不是操作系统文件模型的一部分,而只是某个叫做“rm”的 shell 命令中被人为拼凑进去的一部分。

因此,我必须在脑海中保持两个不同的概念——“删除”文件和“用 rm 删除”文件,并时刻提醒自己,当我的大脑对我的手说“删除它”时,我实际上是在执行哪个操作。

一些 Unix 专家顺着 Phil 的论点推向逻辑上的荒谬,坚持认为最好不要让像 rm 这样的命令变得哪怕稍微友好一点。他们的论点虽然不是用我们所用的表达方式,但认为试图让 Unix 更加友好,赋予它基本的便利设施,实际上会让它变得更糟。不幸的是,他们是对的。

日期:Thu, 11 Jan 90 17:17 CST

主题:不要重载命令!(原主题:Re: rm *)

新闻组:alt.folklore.computers

我们打断本新闻组的正常内容,带来以下信息……

#ifdef SOAPBOX_MODE

请,拜托,请不要鼓励大家用“安全”命令去重载标准命令。

(1)人们通常把它放在 .cshrc 文件错误的位置,导致那些想用“rm”命令删除文件的脚本莫名其妙地要确认,或者误以为文件已被删除,结果把磁盘填满了。

(2)没有办法保护所有可能意外删除文件的情况,如果你保护了一个常用的,用户就会认为“任何操作都可以撤销”(绝对不是真的!)。

(3)如果用户请系统管理员(我现在正干这个活)在终端帮忙,命令不按正常方式运行,当你有用户要帮忙,还有四个“紧急:现在需要处理”的任务排队时,这真的令人抓狂。

如果你想要 rm 命令要求确认,请用:

% alias del rm -i

并且不要用 rm!哎,人们,这有多难?!?

#endif

现在恢复你们原定的“我敲代码敲得那么久,机器只有 0,没有 0 和 1”的讨论……

只是另一个系统黑客

最近,有人在 comp.unix.questions 发布请求,征集系统管理员们最喜欢的管理员恐怖故事。在 72 小时内,收到了 300 条留言。其中大多数都涉及本章所述的方法导致文件丢失。有趣的是,这些都是经验丰富的 Unix 用户,他们本应更为谨慎。更奇怪的是,尽管这些消息中报告了数百万美元的损失,但当 Unix 被批评“不够用户友好”时,绝大多数系统管理员却站出来为 Unix 辩护。

用户不友好?Unix 甚至都不是“系统管理员友好”!例如:

日期:1988 年 9 月 14 日 星期三 01:39 EDT

收件人:RISKS-LIST@kl.sri.

主题:回复:“单击键”

在 Unix 上,即使是有经验的用户也可能因为 rm 造成很大破坏。我从未费心写过安全的 rm 脚本,因为我不会误删文件。然后有一天,我倒霉地敲了 !r 来重复历史命令列表中的某条命令,惊恐地看到屏幕回显 rm -r * ——那是我之前在另一个目录下执行过的命令,那个目录我花时间清理过。

也许 C shell 可以增加一个 nohistclobber 选项?这仍然是我唯一一次误删或覆盖文件,而且这完全是个最低级别的意外陷阱!

巧合的是,就在前几天,我听到一个天真的用户对运行 rm * 删除了他刚刚从邮件中错误创建的文件“*”的恐惧。所幸,按字母顺序排得靠前的文件没有写权限,所以删除操作很快就停止了。

这条信息的作者建议通过给 shell 添加选项“nohistclobber”(禁止历史命令覆盖)来进一步修改,以弥补操作系统对星号名称展开机制的根本缺陷。不幸的是,这种“修补”就像用一层新油漆来修复受水损坏的墙壁,效果差强人意。

始终如一的不如一

可预测的命令共享选项名称,参数顺序大致相同,并且在可能的情况下产生类似的输出。保持一致性需要某个中央机构集中精力制定标准。Macintosh 上的应用程序之所以一致,是因为它们遵循苹果发布的一本指导手册。而 Unix 工具则从未有过这种机构。因此,有些工具的选项前面带有短横线(-),有些则没有;有些读取标准输入,有些则不;有些写入标准输出,有些则不;有些创建的文件是全局可写的,有些则不是;有些报告错误,有些不报告;有些选项与文件名之间有空格,有些没有。

Unix 最初是一次实验,旨在构建一款尽可能简洁干净的操作系统。作为实验,它成功了,但作为生产系统,AT&T 的研究人员逾越了他们的目标。为了让更多人能够使用,操作系统必须功能丰富。如果系统本身不提供这种基本的丰富性,用户就会在底层框架上添加功能。Dave Mankins 认为,Unix 的一致性和可预测性问题的根本原因,可能是它没有为 AT&T 以外的程序员提供用于扩展的知识框架。

日期:1989 年 3 月 4 日 星期六 19:25:58 EST

收件人:Unix 痛恨者

主题:Unix 软蛋们的自娱自乐

Unix 软蛋们喜欢吹嘘每个命令的概念简单性。大多数人可能认为是子程序的东西,Unix 软蛋却把它们包成完整命令,带有自己的参数语法和选项。

这并非完全是蠢主意,因为在没有其他解释器的情况下,通过将这些小子程序连接起来,可以写出相当强大的程序。

可惜没人想到把这些命令做成真正的子程序,这样你就可以把它们链接到自己的程序中,而不用自己写正则表达式解析器(这也是为什么 ed、sed、grep 和各种 shell 对正则表达式的理解相似但又略有不同的原因)。

Unix 审美的最高成就就是拥有一款只做一件事且做得很好的命令。纯粹主义者反对的是,在伯克利的初级程序员折腾过之后,用于将多个文件连接输出 的程序 cat 现在有了选项。(正如 Rob Pike——也许是终极 Unix 极简主义者所说:“Cat 从伯克利回来时挥舞着旗帜。”)

这种哲学在业余者手中,导致了令人难以理解且令人昏昏欲睡的混乱,比如有两款程序 head 和 tail,它们分别打印文件的开头部分或结尾部分。尽管它们的操作是互补的,但 head 和 tail 是不同的程序,由不同作者编写,并且选项也不同!

如果热力学定律在这里适用,那么 Unix 就会像其他那些随着时间慢慢堆积演化的系统一样,拥有同样缺乏一致性的结构和相同程度的熵,也不会比它们更好或更差。然而,架构上的缺陷却进一步加剧了混乱和意外的程度。特别是,程序被禁止访问调用它们的命令行,以防它们“自燃”。Shell 充当了中介的角色,根据用户的输入对命令行进行清洗和重构。不幸的是,Shell 的表现更像克鲁索探长(Inspector Clouseau),而不是弗洛伦斯·南丁格尔(Florence Nightingale)。

我们提到过,Shell 会执行通配符展开操作,也就是说,它会将星号(*)替换成目录中所有文件的列表。这是缺陷一:程序本应调用一个库来执行通配符展开。

按照约定,程序会把选项作为第一个参数,通常前面带一个短横线(-)。这是缺陷二:选项(开关)和其他参数本应是不同的实体,就像在 VMS、DOS、Genera 和其他许多操作系统中一样。

最后,Unix 的文件名几乎可以包含任何字符,包括不可打印字符。这是缺陷三。

这些架构决策之间会产生糟糕的相互作用。当 Shell 在展开 * 时,它会按字母顺序列出文件名,而短横线(-)在这种字典顺序的等级制度中排在最前。因此,当使用 * 时,以短横线开头的文件名会最先出现。这些文件名会被当作传递给程序的选项,从而导致不可预测、令人惊讶,甚至危险的行为。

日期:1990 年 1 月 10 日 星期三 10:40 CST

主题:回复:rm *

新闻组:alt.folklore.computers

还有一名可怜学生的故事,他的主目录里正好有一个名为 -r 的文件。他想要删除所有非目录文件(我猜是这样),于是他输入了:

% rm *

……没错,它确实删除了除了那个可爱的 -r 文件以外的所有东西……

幸运的是,我们的备份系统还算不错。

有些 Unix 受害者把这个“将文件名当成选项”的 bug 变成了一个“特性”,通过在目录中放一个名为 -i 的文件来实现。输入 rm * 时,shell 会将其展开为 rm -i 文件名列表,理论上它会在删除每个文件前请求确认。这个解决方案倒也不坏——前提是你不介意在每个目录里都放一个名为 -i 的文件。

也许我们应该修改 mkdir 命令,让它自动创建这个 -i 文件。然后再修改 ls 命令,使它不显示它。

令人头痛的文件名

我们认识好几个人,在重命名文件时打错字,结果创建了一个以破折号开头的文件名:

% mv 文件1 -文件2

现在试着把它改回来:

% mv -文件2 文件1
usage: mv [-if] f1 f2 or mv [-if] f1 ... fn d1
(‘fn’ 是文件或目录)

这个文件名在其他 Unix 命令中并不会出问题,因为 Unix 命令之间几乎没有一致性。例如,文件名“-文件 2”对 Unix 的“标准文本编辑器”ed 来说是完全合法的。下面这个例子就能正常工作:

% ed -文件2
4347

但即使你把文件另存为其他名字,或者决定彻底放弃这个文件、只想把它删掉,你的困境依然存在:

% rm -文件
usage: rm [-rif] 文件 ...
% rm ?文件
usage: rm [-rif] 文件 ...
% rm ?????
usage: rm [-rif] 文件 ...
% rm *文件2
usage: rm [-rif] 文件 ...
%

rm 会将文件名的第一个字符(破折号)解释为命令行选项;然后它会抱错字符“l”和“e”不是合法的选项。一个文件名以连字符开头——尤其是在这个破折号是通配符匹配的结果时——却被当作一连串选项来看待,这难道不有点疯狂吗?

Unix 提供了两种彼此独立且不兼容的临时“黑科技”方法,用来删除这个命名错误的文件:

% rm - -文件
% rm ./-文件

rm 的 man 手册中说明,一个单独的破折号位于 rm 命令和其第一个文件名之间时,会让 rm 把所有后续的破折号都当作文件名,而不是选项。但不知出于什么原因,rm 和它的近亲 mv 的用法说明中都没有列出这个“特性”。

当然,用破折号来表示“请忽略所有后续破折号”的做法,并不是通行的惯例,因为命令解析是每个程序各自处理的,并没有使用统一的标准库。像 tar 这样的程序,会用破折号来表示标准输入或标准输出。而其他一些程序则干脆无视它:

% touch -文件
touch: bad option -i
% touch - -文件
touch: bad option -i

逗乐你的朋友!让你的敌人抓狂

Unix 命令经常给出看似合理的结果:只有当你尝试去使用它们时,你才会意识到它们实际上是多么荒谬。

next% mkdir foo
next% ls -Fd foo
foo/
next% rm foo/
rm: foo/ directory
next% rmdir foo/
rmdir: foo/: File exists

这里有个逗乐和取悦你朋友的方法(感谢 Leigh Klotz 提供)。

首先,悄悄地做以下操作:

% mkdir foo
% touch foo/foo~

然后把这些咒语的结果展示给你的“受害者”看:

% ls foo*
foo~
% rm foo~
rm: foo~ nonexistent
% rm foo*
rm: foo directory
% ls foo*
foo~
%

最后,为了获得真正的快乐,试试这个:

% cat - - -

(提示:按三次 Ctrl-D 就能返回提示符!)

在线文档

人们投票选总统的次数比阅读印刷文档的次数还多。唯一真正起作用的文档,是那些在线上、只需按一个按键或点击鼠标即可获得的内容。Unix 文档的现状,以及其严重不足的程度,在本书中有专门一章讨论,这里我们仅指出 Unix 的 man 系统在最需要帮助的地方——新手用户面前——表现最差。

并非所有命令都是平等的:有些是由 shell 调用的独立程序,有些则是内置于 shell 中的命令。部分命令有自己的 man 页面,而有些则没有。Unix 期望你知道它们的区别。例如,wc、cp 和 ls 是 shell 之外的程序,有对应的 man 页面;但 fg、jobs、set 和 alias(这些名字哪来的?)则是 shell 内置命令,因此没有自己的 man 页面。

新手在被告知用“man 命令”查看文档时,很快会感到困惑,因为有些命令有文档,有些则没有。如果她使用的 shell 和第三方书籍中介绍的不同,想要理解更是难上加难,除非去请教专家。

错误消息和错误检查,压根没有

新手难免会出错,要么用了错误的命令,要么使用了正确的命令却带了错误的选项和参数。计算机系统必须能够检测这些错误并反馈给用户。不幸的是,Unix 程序很少这样做。相反,Unix 似乎刻意让错误相互叠加,最终导致致命后果。

在上一节中,我们展示了用 rm 命令不小心删除文件是多么容易。但你可能没有意识到,不用 rm 命令,也同样很容易删除文件。

要删除你的文件,试试编译器吧

某些版本的 cc 编译器经常会“坑”本科生,在检查输入文件的明显问题之前就删除之前的输出文件。

日期:1992 年 11 月 26 日 星期四 16:01:55 GMT

主题:求助!

新闻组:cs.questions9

机构:计算机科学基础实验室,

英国爱丁堡

我刚才执行了:

% cc -o doit.c doit  

而不是:

% cc -o doit doit.c  

不用说,我丢了文件 doit.c。

有没有办法能找回来?(从今天早上开始这个文件经过了大量修改)。

:-(

其他程序也表现出类似行为:

收件人:UNIX 痛恨者

日期:1993 年 7 月 1 日 星期四 09:10:50 -0700

主题:被 tar 和羽毛伺候了

经过多次尝试,我终于通过一条不稳定的欧洲链路 ftp 下来了这个 3.2MB 的文件。现在要解压它。

我输入:

% tar -cf thesis.tar  

…却没有任何反应。

哎呀。

是用 c 而不是 x吗?

是的。

tar 因为没有指定文件而报错了吗?

没有。

tar 发现问题了吗?

没有。

tar 真的是没有打包任何文件吗?

是的。

tar 覆盖了 tar 文件变成了垃圾吗?

当然,这就是 Unix。

我要不要再浪费 30 分钟从欧洲重新下载这个文件?

当然,这就是 Unix。

太神奇了。我确信这个设计缺陷已经让很多人吃过亏。有很多简单的方法可以避免这种损失:错误报告、文件版本号、二次确认用户是否真的要覆盖已有文件,等等。感觉他们就是故意努力营造这种损失。

这个漏洞尤其严重地影响那些使用 tar 来备份系统的系统管理员。不止一个系统管理员在备份脚本中写成了 tar xf … 而不是 tar cf …。

这是一个诚实的错误。磁带在旋转。系统管理员几乎不会怀疑 tar 实际上是在尝试从磁带读取指定的文件,而不是把它们写入磁带。事实上,一切看似都在按计划进行,直到有人真正需要恢复某个文件。然后,惊喜来了:备份根本不是备份。

由于几乎没有错误检查,许多“程序员的工具”给高级用户提供了多种丢失重要信息的可能。

日期:1992 年 10 月 4 日 星期日 00:21:49 PDT

收件人:Unix 痛恨者

主题:这么多混蛋可以选择……

我有个程序,叫 foo,持续运行在我的机器上,提供网络服务,并且每 24 小时检查点保存其(庞大)内部状态。

我切换到包含该程序运行版本的目录,由于这不是程序的开发目录,我好奇到底运行的是哪个版本的代码。该代码用 RCS 维护,所以我自然尝试输入:

% ident foo  

以查看可执行文件中包含了哪些源文件的版本。[别管 RCS 明显不合适,或者 ident 的工作方式极其野蛮;我还有更重要的事……]

当然,这次我打错了字,因为手指自动反应,更喜欢单词 indent 而不是无意义的 ident:

% indent foo  

结果,indent 是 UNIX 那个脑残的 C 代码格式化工具的名字。写这个烂东西的混蛋有没有考虑检查它的输入是否是 C 文件(比如,天啊,检查文件名是不是以 .c 结尾)?我想你已经知道答案了。更进一步,这个混蛋还决定,如果给 indent 只传一个参数,那你一定是想直接在源文件上做格式化,覆盖旧文件内容。

不过别担心,混蛋知道你可能担心这么做会破坏文件,所以它会保存一份旧内容的备份到 foo.BAK。它是直接把 foo 重命名成 foo.BAK 吗?当然不是,它傻乎乎地把 foo 的所有内容复制到 foo.BAK,然后截断 foo 文件,再写入新的格式化代码。

混蛋。

你现在应该明白这个故事的重点了……

当一款 Unix 程序运行时,如果它正在从它的可执行文件分页(paging out),你要是乱动文件的各个部分,它会非常生气。特别是它会崩溃,且无法恢复。我因此丢失了程序 20 小时的状态变更。

当然,那些设计(咳)Unix 的混蛋们根本不关心像版本化文件系统这种复杂东西,否则我的命就能救回来了。那些混蛋们也根本想不到要锁定任何你当前正在分页的文件,对吧?

混蛋这么多,干脆全杀了算了?

Pavel

想象一下,如果有一种外墙油漆,在干燥时会释放氯气。根据说明,在外面用没问题,但你如果用它来粉刷卧室,可能会因此丧命。你觉得这种油漆能在市场上撑多久?肯定撑不到 20 年。

报错的笑话

当服务员把一盘满是碟子的托盘掉到地上时,你会笑吗?Unix 粉丝会笑。

他们总是第一个嘲笑那些无助的用户,试图理解那些根本和他们刚输入的内容无关的错误信息。

有人把 Unix 中一些荒谬的错误信息当作笑话发布出来。以下这些 Unix 双关语在 Usenet 上传播,没有署名作者。它们适用于 Csh。

% rm meese-ethics
rm: meese-ethics 不存在

% ar m 上帝
ar: 上帝不存在

% "你如何评价 Dan Quayle 的无能?
引号不匹配。

% ^性别转换^ 操作进行得怎么样?
修改失败。

% 如果我每次国会花费 $ 都有 (,我会有什么?
( 太多。

% make love
Make:不知道如何 make love。停止。

% 和我睡觉
非法字符

% 能借个火吗?
无匹配项。

% man:你为什么离婚?
man:参数过多。

% ^什么是糖精?
错误的替换。

% %blow
%blow:没有这个任务。

这些幽默尝试适用于 Bourne shell:

$ PATH=pretending! /usr/ucb/which sense  
在 pretending 毫无意义!  

$ drink <bottle; opener  
bottle:无法打开  
opener:未找到  

$ mkdir matter; cat >matter  
matter:无法创建  

Unix 的态度

我们描绘了一幅相当惨淡的画面:晦涩难懂的命令名称、不一致且不可预测的行为、缺乏对危险命令的保护、勉强及格的在线文档,以及对错误检查和健壮性的松懈态度。访问 Unix 之家的人不会有愉快的体验。他们像是第三世界联合国救援任务的访客,而不是迪士尼乐园的游客。Unix 是怎么变成这样的?部分原因是历史因素,正如我们已经指出的。但答案还有另一部分:多年来构建和扩展 Unix 的人的文化。这种文化被称为“Unix 哲学”。

Unix 哲学并不是贝尔实验室和 Unix 系统实验室发布的书面建议,它是一种自由流动的道德准则。不同作者列举了它的不同属性。Don Libes 和 Sandy Ressler 在《Life with Unix》(Prentice Hall,1989)一书中对此做了特别好的总结:

  • 小即美。

  • 10% 的工作解决了 90% 的问题。

  • 面对选择时,选择更简单的方案。

根据对 Unix 程序和工具的经验观察,更准确地总结 Unix 哲学是:

  • 小程序比功能齐全或正确的程序更受欢迎。

  • 粗制滥造完全可以接受。

  • 面对选择时,选择逃避。

Unix 并没有哲学,只有一种态度。那种态度认为,简单且半途而废的工作比复杂且执行良好的工作更有价值。那种态度宣称程序员的时间比用户的时间更重要,即使每个程序员对应成千上万的用户。它是一种赞美最低公分母的态度。

日期:1989 年 12 月 24 日 星期日 19:01:36 EST

来自:David Chapman <zvona@ai..mit.edu>

收件人:Unix 痛恨者

主题:终止作业;Unix 设计范式。

我最近学会了如何在 Unix 上终止作业。在此过程中,我学到了很多关于 Unix 的智慧和强大之处,想和你们分享。

当然,你们大多数人并不使用 Unix,所以知道如何终止作业可能没什么用。然而,像我这样的一些人可能会偶尔运行 TeX 作业,这种情况下,知道如何终止作业就至关重要。无论如何,“kill”命令背后的设计原则在整个 Unix 中被严格应用,所以这条信息可能更具普遍意义。

Unix 允许你用 ^Z 挂起一个作业,或用 ^C 退出并终止。然而,LaTeX 会捕获 ^C。结果,我过去经常堆积几十个 LaTeX 作业。这倒不是真的让我困扰,但我觉得还是学会如何清理它们比较邻里友好。

大多数操作系统都有“kill”命令,Unix 也有。在大多数操作系统中,kill 命令终止一个进程。而 Unix 的实现更加通用:“kill”命令向进程发送消息。这体现了第一个 Unix 设计原则:

  • 通过使操作完全通用来赋予用户权力。

kill 命令非常强大;它允许你向进程发送各种消息。例如,你可以向进程发送一个让它自我终止的消息,这个消息是 -9。-9 当然是最大的单数字消息,这体现了另一个重要的 Unix 设计原则:

  • 选择反映功能的简单名称。

据我所知,在所有其他操作系统中,没有参数的 kill 命令会终止当前作业。然而,Unix 的 kill 命令总是需要一个作业参数。这个明智的设计选择体现了另一个明智的设计原则:

  • 通过要求长命令和危险操作的确认,防止用户意外搞砸自己。

这个原则在 Unix 中的应用极为广泛且文档完善,这里我就不详细说明了,只是顺便提及 Unix 中的注销和文件删除实现。

在我所知的所有其他操作系统中,kill 命令的作业参数是作业名称。这是一个不充分的接口,因为你可能会有多个 LaTeX 作业(例如),它们的名字都是“latex”,因为它们都是 LaTeX 作业。因此,kill -9 latex 会产生歧义。

像大多数操作系统一样,Unix 有一个列出你作业的命令,助记符叫“jobs”。jobs 命令的输出大致如下:

zvona@rice-chex> jobs  
[1] - Stopped latex  
[2] - Stopped latex  
[3] + Stopped latex  

这让你很容易将特定的 LaTeX 作业与作业号对应起来,作业号显示在方括号内。

如果你的思维受到设计不佳操作系统的影响,你可能会认为 kill -9 1 会终止列表中的第 1 号作业。但你会发现它实际上会给你一个友好的错误信息:

zvona@rice-chex> kill -9 1  
1: not owner  

kill 命令正确的参数是进程 ID。进程 ID 是像 18517 这样的数字。你可以用“ps”命令找到作业的进程 ID,“ps”命令会列出作业及其进程 ID。找到正确的进程 ID 后,你只需:

zvona@rice-chex> kill -9 18517  
zvona@rice-chex>  
[1] Killed latex  

注意 Unix 会先给你提示符,然后才告诉你作业已经被终止。(用户输入会出现在以“[1]”开头的行之后。)这体现了另一个 Unix 设计原则:

  • 只告诉用户他需要知道的信息,并且不要提前告诉。不要用多余信息增加用户的认知负担。

希望这个小练习对你有所启发。我从学习中深刻体会到 Unix 的设计哲学。Unix kill 命令的优雅、强大和简洁应该成为我们所有人的一课。

发件人:(Dave Jones)

发件人:

发件人:Phil Agre

来自:(Randal L. Schwartz)

发件人:Matthew P Wiener

发件人:

发件人:(Kees Goossens)

发件人:(Tommy Kelly)

发件人:Daniel Weise

来自:Pavel Curtis

djones@megatest.uucp
ram@attcan.uucp
agre@gargoyle.uchicago.edu
merlyn@iwarp.intel.com
weemba@garnet.berkeley.edu
dm@think.com
kgg@lfcs.ed.ac.uk
tk@dcs.ed.ac.uk
daniel@dolores.stanford.edu
pavel@parc.xerox.com