一本很有趣的书, 是那些用惯UNIX的人对UNIX的各种指责,诋毁,谩骂和嘲笑. 是由爱而生的恨. 即使当成一本高级笑话书,也是很有价值的.
UNIX痛恨者手册
By Simson Garfinkel, Daniel Weise, Steven Strassmann
第一章 UNIX
世界上第一个电脑病毒
“伯克利的两项最著名的产品是UNIX和LSD (一种毒品),我想这不是巧合”
病毒依赖于微小的个体和强大的适应性得以生存。它们并不复杂:它们没有为呼吸,新陈代谢,肌体活动等功能提供什么,只有足够的DNA或RNA以供繁衍。比如,肺炎病毒比起它们入侵的细胞要小得多,但它们在每个肺炎流行季节都能够产生新的变种,造成无数人死亡。
一个好病毒的特点是:
* 个头小
病毒做的事情不多,所以不需要很大。有人认为病毒不是生物,只是一些有破坏性的酸和蛋白质。
* 可移植性
病毒经常变异,以便以不同的方式攻击不同的细胞。据说AIDS就是由猴子身上的病毒变异而成的。
* 耗尽寄主的资源
* 快速变异
UNIX具有以上所有优点。在它刚诞生时,很小,功能不多,缺乏真正操作系统所需要的功能(如文件映射,告诉IO,健壮的文件系统,设备锁,合理的进程间通讯),它的移植性很好。UNIX耗尽主机的资源,没有系统管理员的时时呵护,UNIX会不断恐慌(panic),core dump,挂起。UNIX不断变异:同一个补丁在一个版本上工作,在另一个版本上就不行。
UNIX是有用户界面的计算机病毒。
标准化那些不一致的
--------------------------------------------------------------------------------
“标准的伟大之处在于它可以有很多” --- Grace Murray Hopper
自从UNIX 80年代开始流行以来,UNIX厂商一直在努力进行UNIX标准化工作。SUN, IBM,HP和DEC在这个他们自己制造的难题上倾注了数百万美元。
为什么UNIX厂商不喜欢UNIX标准化?
许多用户受够了复杂繁多的UNIX,最终只好使用Windows,因为他们的这个UNIX无法支持那个UNIX上的应用程序。
如果UNIX标准化了,谁还会买SUN的机器呢
标题: 第二章 欢迎新用户
欢迎新用户如同用一把上了六颗子弹的左轮枪玩俄罗斯轮盘赌Ken Thompson 自己设计过一辆汽车。和其他车不同,它没有速度计、汽油计,也没有那些愚蠢的指示灯讨司机的厌。如果司机犯了什么错误,仪表盘上就会出现一个大大的 “?”。“有经验的司机,”Thompson说,“应该知道哪儿搞错了。”
计算机系统的新手需要一个友好的系统。至少,一个得体的系统会这样招待自己的客人:
与功能有逻辑关系的命令名
对危险命令的小心处理
一致的命令行为和命令行参数解析
易得和易读的在线文档
当命令失败时,给出可理解和有用的错误反馈
在建造UNIX的过程中,从没邀请过住户。来访的都是些戴着安全帽的建筑工人,被安插在这个破木板房子的各个角落。不幸的是,不仅没有人性因素(human factors)工程师的参与,而且住户的需要就从来没有被考虑过。所以抽水马桶、中央供暖、窗户等这些方便设施在后期就很难再添加了。但是建筑师们仍然为UNIX的设计而骄傲,似乎他们并不介意在一个没有烟火探测器的屋子里睡觉。
在其发展的大部分历史中,UNIX只是大学和工业研究人员的研究工具。随着大批便宜工作站的出现,UNIX作为平台软件进入了新时代。这一变化大约发生在1990年,其标志就是工作站厂商把C编译器从UNIX发布中剔除出去,以降低成本满足非开发用户的需求。可见,只是最近几年中UNIX厂商才开始考虑非程序员用户的需要,开始为他们提供shell以外的图形界面。
含糊的命令名
UNIX新手总是对UNIX对命令的命名表示惊讶。在DOS和Mac上受的教育不足以让他们体会到cp、rm、ls这类两字母命令的简洁和优美。
像我们这样用过70年代早期的IO设备的人都能理解,ASR-33 Teletype这类设备的速度、可靠性,以及它的键盘是万恶之源。和今天这种基于反馈原理、只需要关闭一个微开关的键盘不同,你必须用足力气揿下 Teletype的键至少半英寸,以发动一个类似自行车上用的小型发电机,在上面操作要冒指骨骨折的危险。
如果当时Dennis和Ken用的是Selectric而不是Teletype,可能今天我们敲的将不是”cp”和”rm”而是”copy”和” remove”了。(Ken Thompson曾被问道如果他能重新设计UNIX他将做什么修改,他回答说:“我会在creat命令后加上个e。”),科技在拓宽我们的选择的同时,也能限制我们的选择,此一例也。
20多年过去了,还有什么理由延续这一传统呢?理由就是“历史的无可替代的力量”,历史就是那些存在的代码和教科书。如果一个厂商用remove 替代了rm,那么所有UNIX教科书就不适用于这一系统了,每个使用rm的shell脚本都需要被修改。而且这也不合POSIX标准。
一个世纪前,打字高手由于击键过快,经常把打字键柄搅在一起,工程师设计了QWERTY键盘,于是问题得到了解决,因为没人能在这样的键盘上打得快。计算机的键盘不再有机械键柄,但QWERTY的键盘布局仍然在使用。同理,在未来的一个世纪中,我们仍然会继续使用rm。
事故会发生
用户十分关心自己的数据和文件。他们使用计算机来产生、分析和存储重要信息。他们相信计算机能够保护他们的重要财产。如果没有了这种信任,他们和计算机的关系就会蒙上阴影。UNIX辜负了我们的信任,它拒绝对使用危险命令的用户提供保护。比如rm就是以删除文件为目的的危险命令。
所有UNIX新手都有不小心无可挽回地删除重要文件的经历,即使是专家和系统管理员也遇到过。因此而每年损失的时间、精力可能价值几百万美元。这是个值得解决的问题;我们不理解为何UNIX一直拒绝解决这一问题。难道结果还不够悲惨么?
UNIX比其他操作系统更需要提供恢复删除功能,原因是:
UNIX文件系统没有版本功能
自动的版本维护能保留文件的历史版本,防止新版本冲掉老版本。
UNIX程序员在错误处理方面臭名昭著许多程序不检查是否所有内容都被写入了磁盘,或被写入的文件是否存在。有些程序总是删除输入文件。
UNIX shell扩展“*”,而不是其子命令
于是rm这样的命令就无法检查“*”这些危险的参数。即使是DOS也对”del *.*”有些提示。但在UNIX下,rm * 和 rm file1 file2…是没有区别的。
删除是永久的
UNIX没有undelete命令。许多其他更安全的系统则只是标记被删除文件所用的块为“可被使用”,然后把它移到一个特殊目录下。如果磁盘满了,这些文件块才会被重新使用。这一技术不是什么火箭科学,Macintosh在1984年就提出了“回收站”的想法,而Tenex早在1974年就采用了这一技术。连DOS也提供了简单的undelete功能,虽然并不总有效。
这四个问题互相合作,制造了无数无法恢复的重要文件。解决的方法早就存在,但UNIX“标准”版中却从来没有提供。
欢迎来到未来世界。
“rm”就是终结
许多实际的恐怖故事说明了以上的这些原则。以下是alt.folklore.computers新闻组上流传的一系列故事中的一个:
Date: Wed, 10 Jan 90
X-Virus: 6
From: djones@megatest.uucp (Dave Jones)
Subject: rm *
Newsgroups: alt.folklore.computers
是否有人曾想执行以下命令:
% rm *.o
结果却打成了:
% rm *>o
现在你得到了一个空文件o,以及大量的空间来存放它!
事实上,你可能连o也得不到,因为shell的文档并没有说o是在*被扩展前还是被扩展后被建立的。
上回书说到如何用rm获得一个空文件和很大的磁盘空间,下面是另一种用法:
Date: Wed, 10 Jan 90
X-Virus: 6
From: ram@attcan.uucp
Subject: Re: rm *
Newsgroups: alt.folklore.computers
我也被rm搞过。有一次我想删除一些/usr/foo/下的东西,我在/usr/foo下敲了以下命令:
% rm –r ./etc
% rm –r ./adm
当我要删除./bin目录时,我忘敲了那个点。我的系统似乎不太喜欢这个。
当受了这一致命一击后,UNIX就彻底完蛋了。聪明的系统会给用户一个恢复的机会(或至少提醒用户这一操作会导致系统崩溃)。
UNIX迷认为偶尔的文件误删除是正常的。比如,可以参考以下下面这个comp.unix.questions上的FAQ:
6) 如何反删除一个文件?
也许有一天,你不小心执行了一下这个命令:
% rm * .foo
然后发现你把“*”删掉了。你应该把这当作人生的一课。
当然称职的系统管理员应该定时备份系统。所以你最好问问他们手中是不是有你的文件备份。
“人生的一课”?没有任何一个其他厂商用这样的态度对待一个有缺陷的产品。“大人,我知道您的油箱炸了,但这是人生的一课。”“陪审团的先生女士们,我们将证明电锯保险开关的失效不过是给用户上的人生的一课。”不错。
改变rm的行为也不是个办法
被rm咬了几次后,往往会想到用”rm -i”替换rm,或整个替换掉rm,把所有被删除的文件放到~/.deleted目录中。这些小技巧让用户有了错误的安全感。
Date: Mon,16 Apr 90 18:46:33 199
X-Virus: 6
From: Phil Agre <agre@gargoyle.uchicago.edu>
To: UNIX-HATERS
Subject: deletion
在我们的系统上,“rm”并不真正删除文件,而是给文件换了名,这样”undelete”(不是unrm)这样的工具就能恢复被删的文件。
这个功能让我不再对删除文件多加小心,反正删掉了也能找回来。可是,我错了。Emacs中的删除并不支持这个功能,Dired命令也是如此。这当然是因为文件恢复并不是操作系统的一个功能。
所以,现在我脑子里有两个概念,一个是”deleting”一个文件,一个是”rm’ing”一个文件。当我的手要我的脑子删除一个文件时,我总要把这两个概念区分一遍。
一些UNIX专家由此得出了荒谬的结论,他们认为最好别把rm搞得更友好。他们争辩说,让UNIX更友好的努力往往适得其反。不幸的是,他们是对的。
Date: Thu, 11 Jan 90 17:17 CST
X-Virus: 6
From: merlyn@iwarp.intel.com (Randal L. Schwartz)
Subject: Don’t Overload commands! (was Re: rm *)
Newsgroups: alt.folklore.computers
请千万别让人用“安全”命令去替换标准命令。
(1) 许多shell程序会对多嘴的rm感到惊讶,而且也不会想到删除了的文件仍然占有磁盘空间。
(2) 并不是所有删除操作都是安全的,有户会因此产生一切都能恢复的错觉。
(3) 那些不标准的命令对系统管理员来说尤其可恨。如果你想有个有确认功能的”rm”,用下面的命令:
% alias del rm -i
千万别替换rm!
最近,comp.unix.questions上有过一次对系统管理员的调查,让他们?*鲎羁植赖南低彻芾砉适隆?2小时内,就有了300多条回应。许多和我们上面描述的文件删除有关。可笑的是,这些可是UNIX高手。然而正是他们在对“UNIX对用户不友好”这类指责进行着辩护。
对用户不友好?UNIX对系统管理员又友好过么?请看
Date: Wed, 14 Sep 88 01:39 EDT
X-Virus: 6
From: Matthew P Wiener <weemba@garnet.berkeley.edu>
Subject: “Single Keystroke”
在UNIX上,即使是有经验的用户也会误用rm。我从来没有误删除过文件,可是有一天,我用!r重复执行一个历史命令,我惊讶地发现被运行的是”rm –r *”。
为什么不能有个没有history功能的shell?
我还听到过一个用户试图删除一个名叫”*”的文件,好在他没有权限。
这个用户还想修改shell来避免对*进行展开。不幸的是,这个补救如同是在渗水的墙上再刷一层漆,治标不治本。
在线帮助
用户读打印文档的次数比他们参加选举投票的次数还要少。只有触手可及的在线文档才是有用的。下面我们看看UNIX的man是如何让最需要它的新用户失望的。
不是每个命令都是平等的,有些是外部命令,有些是内部命令。有些有man page,有些没有。UNIX要求你能区分这些命令。比如,wc, cp和ls是外部命令,它们都有man page,而fg, jobs, set和alias(这些长文件名是从哪里来的?)是内部命令,它们没有man page。
UNIX告诉新手用”man command”命令获得帮助,他们可不知道并不是所有命令都是如此。另外,如果他们的shell设置得有些不标准,他们就只能请教高手来获得帮助了。
错误信息和错误检查?没门!
新手很容易犯错误,比如用错命令,或用错选项。系统应该能识别这些错误,并且反馈给用户。不幸的是,UNIX程序从来不麻烦自己。相反,UNIX往往把各种错误混在一起,直到产生致命的结果。
上面一节我们说明了rm如何容易造成误删除。但你可能不知道不用rm也能很容易地误删除文件。
想删除你的文件么?试试编译器
一些cc版本经常根本不考虑用户的可能输入错误,而删除一些源代码文件。一些本科生常常着了道。
Date: Thu, 26 Nov 1992 16:01:55 GMT
X-Virus: 6
From: tk@dcs.ed.ac.uk (Tommy Kelly)
Subject: HELP!
Newsgroups: cs.questions
Organization: Lab for the Foundations of Computer Science, Edinburgh UK
我刚才想这么编译程序:
% cc –o doit doit.c
不小心敲成了:
% cc –o doit.c doit
不用说我的doit.c被冲掉了。有没有办法能恢复我的程序?(我干了整整一个上午)
其他一些程序也有同样的行为:
Date: Thu, 1 July 1993 09:10:50 - 0700
X-Virus: 6
From: Daniel Weise <Daniel@dolores.stanford.edu>
To: UNIX-HATERS
Subject: tarred and feathered
经过几次努力,我总算从欧洲的一个脆弱ftp站点上下载了了一个3.2M的文件。该untar它了。我敲了一下命令:
% tar –cf thesis.tar
…没有回应。
老天!
是不是该用x选项而不是c?
是的。
tar是不是给出了错误信息说没有指定输入文件?
没有。
tar是否感觉到有什么不对劲?
没有。
tar是不是真的什么也没有tar?
是的。
tar是否把thesis.tar用垃圾覆盖了?
当然,这就是UNIX。
我是不是需要再花 30分钟从欧洲下载这个文件?
当然,这就是UNIX。
我敢肯定有不少人遇到过这一不幸,有那么多的解决办法,比如:错误信息,文件版本化,确认用户是否想覆盖一个已有文件,等等等等。tar似乎在有意给用户找麻烦。
对于经常用tar备份的系统管理员来说,这个bug更是危险。不少系统管理员都曾经在备份脚本中错误地使用过“tar xf…”。在需要恢复备份的时候,才发现原来什么也没做。
欲知是否还有其他这样的恐怖命令,请听下回分解。
上回书说到cc、tar等命令是如何帮助你删除重要文件的。UNIX的强大当然不局限于此。
因为没有错误检查,在众多“UNIX 强大编程工具”的支持下,用户有各种选择来删除他们的重要文件。
Date: Sun, 4 Oct 1992 0:21:49 PDT
X-Virus: 6
From: Pavel Curtis <pavel@parc.xerox.com>
To: UNIX-HATERS
Subject: So many bastards to choose from…
我有一个总在运行的程序foo,用来提供网络服务,并且每隔24小时检查系统内部状态。
一天,我cd到foo所在的目录,因为这不是开发目录,我想看看foo的版本是多少。代码是由RCS来维护的,所以我自然而然地使用了以下命令:
% ident foo
先别管RCS的种种劣迹,也别管ident如何原始疯狂。我这次的麻烦是,我的手指自行其是地选择了更像一个词的indent而不是ident:
% indent foo
indent是UNIX的一个愚蠢的C代码风格转换工具。那个写indent的混蛋是否判断了一下输入文件真的为C程序么 (天哪,至少可以看看文件的后缀是否为.c吧)?我想你知道答案。而且,这个SB(Said Bastard)认为如果你只给了一个参数,那么你就是想要进行在线风格转换。不过别着急,这个SB 考虑到了可能带来的麻烦,他保存了一个备份foo.BAK。然而他是否只是简单地把foo换了个名字呢?没有,他选择了拷贝(毫无疑问,那个写 indent的程序员在准备备份的时候已经打开了foo,而且rename系统调用是后来才有的)。
现在,你可能知道发生了些什么了…
我那正在运行中的foo在准备页面扇出的时候,发现原来的可执行文件已经不在了,这可不是什么好事,于是我的foo崩溃了,我丢掉了20小时的系统状态信息。
自然那些设计(咳嗽)UNIX的混蛋们对复杂的文件版本化功能不感兴趣,而这一功能就能救我的命。当然,那些混蛋也从未想到过对准备进行页面扇出的文件加锁,是不是?
有那么多混蛋可供选择,为什么不把他们都宰了?
Pavel
想象一种散发氯气的油漆,按照说明,用在户外是不成问题的,但如果用它刷你卧室的墙壁,你的脑袋就要大了。这样的油漆能在市场上存活多久呢?当然不会超过20年。
错误信息笑话
当你看到饭馆跑堂的把一盘菜撒在顾客脑袋上时,你会笑么?UNIX迷会的。但那些无助的用户对着错误信息百思不得其解的时候,他们是最先发出笑声的。
有人整理了一些UNIX最为可笑的错误信息,把他发布在Usenet上。他们使用的是C shell.
% rm meese-ethics
rm: messe-ethics nonexistent
% ar m God
ar: God does not exist
% “How would you rate Dan Quayle’s incompetence?
Unmatched “.
% ^How did the sex change^ operation go?
Modifier failed.
% If I had a ( for every $ the Congress spent, what would I have?
Too many (‘s
% make love
Make: Don’t know how to make love. Stop.
% sleep with me
bad character
% got a light?
No match
% man: why did you get a divorce?
man:: Too many arguments.
% ^What is saccharine?
Bad substitute.
% %blow
%blow: No such job.
下面的这些幽默作品来自Bourne Shell:
$ PATH=pretending! /usr/ucb/which sense
no sense in pretending
$ drink <bottle; opener
bottle: cannot open
opener: not found
$ mkdir matter; cat >matter
matter: cannot create
UNIX态度
我们展现了一个非常惨淡的图景: 迷一般的命令名,不一致和无法预计的运行结果,危险命令没有保护,无法接受的在线文档以及在错误检查和容错性方面的稀松工作。那些参观UNIX的人不是为了得到热情款待,他们不是迪斯尼公园中的游客,更像是执行任务中的联合国维和部队。UNIX怎么会搞成这个样子?如我们曾指出的那样,其中有一些是历史原因造成的。但是还有其他的原因:那就是多年来形成的UNIX文化,这种文化被称为 “UNIX哲学”。
UNIX哲学不是来自Bell实验室或UNIX系统实验室的手册。他是自然形成的,其中包含了许多人的贡献。Don Libes和Sandy Ressler在《UNIX生活》(Life with UNIX)中对UNIX哲学作了很好的总结:
小即是美
用10%的工作解决90%的任务
如果必须作出选择,选择最简单的那个。
根据UNIX程序和工具的实际表现来看,对UNIX哲学更为精确的总结应该是:
小的程序比正确的程序更好
粗制滥造是可以接受的
如果必须作出选择,选择责任最小的那个。
UNIX没有哲学,UNIX只有态度。这个态度指出简单的做了一半的工作比复杂完整的工作更好。这个态度指出程序员的时间比用户的时间更为珍贵,即使用户比程序员要多得多。这个态度指出达到最低要求就足够了。
Date: Sun, 24 Dec 89 19:01:36 EST
X-Virus: 6
From: David Chapman <zvona@ai.mit.edu>
To: UNIX-HATERS
Subject: killing jobs; the Unix Design Paradigm
我最近学会了如何在UNIX上杀掉任务。在这个过程中我体会到了不少UNIX的强大和智慧,我想应该和大家分享一下。
你们中的大多数当然不用UNIX,所以知道如何UNIX上杀任务估计没什么用。但是,你们中的一些人,包括我,可能会经常运行一些TeX任务,那么学会杀任务就显得尤为重要了。“kill”命令继承了UNIX的设计原则,所以下面的一些体会有更为通用的意义。
在UNIX中你可以用^Z挂起一个任务,或者用^C终止一个任务。但是LaTex自己截获^C。结果是,我经常搞出一堆LaTex任务。我对此到不在乎,可还是觉得应该想办法除掉它们。
许多操作系统有“kill”这样的命令,UNIX也不例外。大多数操作系统上的“kill”仅仅用来杀死进程。但UNIX的设计更为通用:“kill”被用来向进程发送一个信号,这体现了UNIX的一个设计原则:
尽量使操作通用,给予用户强大力量(power)
“kill”命令功能很是强大;你能用它给进程发送各种各样的信号。比如,9这个信号用来杀死进程。注意到9是最大的一位数,这体现了UNIX的另一个设计原则:
使用能体现功能的最简单的名字
在其他我知道的操作系统上,不带参数的“kill”用于杀死当前任务。单UNIX的“kill”总是需要参数。这体现了UNIX的一个明智的设计原则:
尽量使用长参数或提示来防止用户不小心把自己**了(screwing himself)
这一设计原则在许多UNIX应用程序中得到了体现,我不想列举它们,但还是想提一下UNIX上logout和文件删除的实现,希望你知道我的意思。
在其他我知道的操作系统上,“kill”接受的参数是任务名。这不是好的选择,因为你可能有许多LaTex任务同时运行,它们都有同样的任务名“latex”。所以“kill –9 latex”可能会产生歧义。
和其他操作系统一样,UNIX提供一个列出任务的命令“jobs”,下面是一个例子:
zvona@rice-chex> jobs
[1] – Stopped latex
[1] – Stopped latex
[1] + Stopped latex
这样你可以用job号(表示在[]中)标识一个任务。
如果你受到那些未经精雕细刻的操作系统的影响,你会想用“kill –9 1”来杀掉第一个latex任务。但你会发现下面的错误信息:
zvona@rice-chex> kill -9 1
1: not owner
正确的做法是使用进程号,比如18517。你能用“ps”命令获得它。当找到了相应进程号后,你只要:
zvona@rice-chex> kill -9 18517
zvona@rice-chex>
[1] Killed latex
注意到UNIX在你的任务被真正杀掉之前就给了你提示符。这又体现了一个UNIX设计原则:
对于给用户的反馈,能少说绝不多说,能晚说绝不早说。以免过多信息所可能导致的用户脑损伤。
我希望这些体会能对大家有用。在这一学习过程中,我自己当然被UNIX设计哲学所深深吸引了。我们都应该从UNIX kill命令的雅致、强大和简洁中学到些东西。
第二章就这么完了,经历了这么多艰难困苦的你已经不是新手了,下回书将介绍UNIX之文档,或者说UNIX之没有文档.
标题: 第三章 文档
OK,不是新手的你可能想进一步学习了解UNIX。不错,UNIX文档正是你需要的。
文档
什么文档
“使用UNIX进行操作系统教学的一个好处是,学生的书包能装下所有的UNIX源代码和文档。”
—— John Lions, 新南威尔士大学,1976年在谈论UNIX版本6时说的一段话。
多年以来,有三个获得UNIX有关知识的简单途径:
阅读源代码
写一个自己的UNIX
给写UNIX的程序员打电话(或是发email)
和荷马史诗一样,UNIX被口头传诵着。如果不成为内核黑客,你就不可能是一个严肃的UNIX用户——或者至少应该在身边有个触手可及的内核黑客。那个确实存在的文档——man手册——不过是一些已经知道自己在做什么了的人所收集的一些备忘录。UNIX的文档是这么简洁,你能在一下午读完它。
在线文档
man工具是UNIX文档系统的基础。man接受你输入的参数,找到相应的文档文件,把它输出到nroff(还包括一些地球上没有其他地方使用的一些文本格式宏),最后结果被发送到pg或more。
起先,这些零碎文档被叫做”man页”(man pages),因为这些文档多为一页左右(多数情况是少于一页)。
man对于那个时代是个不错的玩意,但那个时代早已一去不复返了。
多年来,man系统不断发展成熟。值得称赞的是,它并没有像UNIX的其他部分一样搞得代码混乱程序难懂,可是它也没变得更有用。事实上,在过去的15年中,UNIX的文档系统只有了两处改进:
catman. 程序员曾“惊喜地”发现除了nroff格式以外,他们还能存储处理过的文档文件,这样文档调出的速度就更快了。 对于今天的快速处理器,catman似乎不那么需要了。
但是许多nroff处理过的文档文件仍然占据着用户的几兆磁盘空间。makewhatis, apropos和key (最终构成了man –k功能)是一个对man手册进行索引的系统,这样即使不知道程序的确切名字也能进行查询。
与此同时,电子出版的势头早已超过了man手册。使用今天的超文本系统你能用鼠标从一篇文章跳到另一篇文章;与之相比,man手册仅仅在末尾提供”SEE ALSO”一节,让用户自己再man下去。在线文档的索引功能又是如何呢?今天你可以买到CD-ROM上的牛津英语词典,它对其中的每一个词都加了索引;可是man手册还是仅仅对命令名和描述行进行索引。今天甚至连DOS都提供了有索引的超文本文档。可是man手册还是采用适合DEC打印终端的80列66 行的格式。
公平点说,有些厂商是在看不下去,提供了自己的超文本在线文档系统。在这些系统上,man手册走到了进化的尽头,常常不是过时了,就是根本不存在。
“我知道它就在这里,可就是找不到”
对于今天还在使用man手册的人来说,最大的问题是告诉man你要的man手册就在那里。在以前,查找man手册是很容易的:全都在 /usr/man下头。后来man手册按章节分成了不同的目录:/usr/man/man1, /usr/man/man2,/usr/man/man3等等。有的系统甚至把“本地”man手册也放在/usr/man/man1下。
当AT&T发布系统V的时候,情况变得费解了。/usr/man/man1目录变成了/usr/man/c_man,似乎字母比数字更好记。在有些系统上,/usr/man/man1变成了/usr/local/man。那些销售UNIX应用程序的公司开始建立自己的man目录。
最终,伯克利修改了man程序使得它能对环境变量$MANPATH中指定的一系列目录进行查找。这是个伟大的想法,只有一个小毛病:它不工作。 (以下省略100字, 因为我太懒了,内容也有些太过时了,Linux上的man还是不错的,除了无法获得shell内部命令的man手册,当然,man bash是一个选择 -- me)。
这个就是内部文档?
一些大的UNIX工具也提供自己的文档。许多程序的在线文档是一行叫人费解的 “使用”(usage)说明。下面是awk的“使用”说明:
% awk
awk: Usage: awk [-f source | ‘cmds’] [files]
是不是挺有用的?复杂一些的程序有着更深入的在线文档。不幸的是,它们有时候描述的似乎不是你正在运行的程序。
Date: 3 Jan 89 16:26:25 EST (Tuesday)
X-Virus: 6
From: Reverend Heiny <Heiny.henr@Xerox.COM>
To: UNIX-HATERS
Subject: A conspiracy uncovered (阴谋被揭露了)
经过几个小时的专心研究,我得出了一个重要的结论:
UNIX是狗屎 (UNIX sucks)
现在,你可能觉得很惊讶,但这是事实。这项研究已经被遍布全球的研究人员所证实了。
更为重要的是,这不仅仅是摊狗屎,而是又稀又粘的臭狗屎,是大写的臭狗屎。看看下面这个例子,你就知道了:
toolsun% mail
Mail version SMI 4.0 Sat Apr 9 01:54:23 PDT 1988 Type ? for help
“/usr/spool/mail/chris”: 3 messages 3 new
>N 1 chris Thu Dec 22 15:49 19/643 editor saved “trash1”
N 2 chris Tue Jan 3 10:35 19/636 editor saved “trash1”
N 3 chris Tue Jan 3 10:35 19/656 editor saved “/tmp/ma9”
& ?
Unknown command: “?”
&
什么样的系统环境(特别是这个到了能开车、投票、喝啤酒年龄的家伙)会拒绝一个它让使用的命令?
为什么用户手册是如此脱离现实?
为什么这些神秘的命令是这么和功能不符?
我们不知道Heiny的问题是什么;和我们上面提到的一些问题一样,这个bug似乎已经被修正了。或者说,它被转移到了其他程序中。
Date: Tuesday, September 29, 1992 7:47PM
X-Virus: 6
From: Mark Lottor <mkl@nw.com>
To: UNIX-HATERS
Subject: no comments needed (无需多说)
fs2# add_client
usage: add_client [options] clients
add_client -i | -p [options] clients
-i interactive mode – invoke full-screen mode
[还有一些选项,这里省略了]
fs2# add_client -i
Interactive mode uses no command line arguments
如何得到真正的文档
实际上,UNIX最好的文档是经常用strings处理程序二进制代码。使用strings你能得到所有程序中定死了的文件名,环境变量,未公开的选项,怪异的错误信息等等。比如,如果你想知道cpp是如何去查找头文件的,你最好使用strings而不是man:
next% man cpp
No manual entry for cpp.
next% strings /lib/cpp | grep /
/lib/cpp
/lib/
/usr/local/lib/
/cpp
next%
嗯…别着急
next% ls /lib
cpp* gcrt0.o libssy_s.a
cpp-precomp* i386/ m68k/
crt0.o libsys_p.a posixcrt0.o
next% strings /lib/cpp-precomp | grep /
/*%s*/
//%s
/usr/local/include
/NextDeveloper/Headers
/NextDeveloper/Headers/ansi
/NextDeveloper/Headers/bsd
/LocalDeveloper/Headers
/LocalDeveloper/Headers/ansi
/LocalDeveloper/Headers/bsd
/NextDeveloper/2.0CompatibleHeaders
%s/%s
/lib/%s/specs
next%
我真笨。NEXTSTEP的cpp使用了/lib/cpp-precomp。你不可能在man手册中发现这些。
next% man cpp-precomp
No manual entry for cpp-precomp.
OK. 这一切究竟是因为什么?这一切究竟是从何而来?下回分解。
上回书说到源代码是最好和唯一的文档,根本原因是因为UNIX是...
给程序员用的,不是用户
别因为UNIX蹩脚的文档而责怪Ken和Dennis。 UNIX刚开始建立文档时并没有遵守业界流行的文档标准,一些bug和潜在的陷阱,而不是程序的功能,被记录了下来,这是因为读这些文档的人往往就是 UNIX系统开发者。对于许多开发者来说,man手册不过是收集bug报告的地方。那些针对初级用户、程序员和系统管理员提供文档的观念是新玩意。可悲的是,由于70年代建立的UNIX文档系统,这一观念实现的并不是很成功。
UNIX世界认识到了这些文档方面的现状,但并不觉得有什么大不了的。《UNIX生活》很客观地说明了UNIX对于文档的态度:
UNIX源代码是最好的文档。毕竟,这是系统用以决定该如何运行时所参照的文档。文档用来解释代码,经常是一些不同的人在不同的时间写成的,而这些人往往不是写代码的人。你应该把这些文档看作是指南。有时候这些文档不过是些期望而已。
但是,更一般的做法是去源代码中寻找未被文档化使用方法和功能说明。有时候你会发现一些文档中记录的功能其实并没有被实现。
这还只是针对用户程序。对于内核,情况就更为糟糕了。直到最近,还没有厂商提供的设备驱动编写和内核级调用函数的文档资料。有人开玩笑说:“如果你觉得需要阅读关于内核函数的文档,那么很可能你本来就不配使用这些函数。”
真相恐怕更为邪恶。之所以没有内核文档是因为AT&T把它的代码看成是商业机密。如果你想写一本说明UNIX内核的书,那么你就等着入被告席吧。
源代码就是文档
命里注定,AT&T的计划弄巧成拙了。由于没有文档,了解内核和应用程序的唯一途径就是阅读源代码。结果是,UNIX源代码在在最初的 20年中被疯狂的盗版。咨询人员,程序员和系统管理员去搞UNIX源代码并不是为了重新编译或制作出售自己的UNIX版本,他们需要文档,而源代码是唯一的选择。UNIX源代码从大学流向周边的高科技公司。这当然是非法的,但是情有可原:UNIX厂商提供的文档不够用。
这并不是说源代码中有什么值钱的秘密。所有读过UNIX代码的人都被下面的一行粗暴注释惊呆过:
/* you are not expected to understand this */ (/* 没指望你能明白 */)
尽管这行注释最开始出现在UNIX V6内核中,但是几乎所有的原始AT&T代码都差不多,其中充满了内联手动优化和怪异的宏。寄存器变量被冠以p, pp和ppp这类的名字。“这个函数是递归的”这样的注释似乎表明递归调用是什么难理解的概念。事实上,AT&T在文档方面好为人师的态度只不过是其写代码的马虎态度的一个反映。
要识别一个蹩脚手艺人其实很简单:你会看到裂缝上的油漆,一个接一个的补丁,所有东西被胶带和口香糖勉强凑合在一块儿。必须承认:如果想从头建立和重新设计什么,必须要多思考,多下功夫。
Date: Thu,17 May 90 14:43:28 -0700
X-Virus: 6
From: David Chapman <zvona@gang-of-four.stanford.edu>
To: UNIX-HATERS
这是man man中的一段,挺有意思:
DIAGNOSITICS
如果你使用-M选项而且给出的路径并不存在,那么输出的错误信息可能有点儿不对。比如/usr/foo/目录不存在,如果你运行:
man –M /usr/foo ls
那么你得到的错误信息是“No manual entry for ls”(“没有ls的手册记录”)。正确的错误信息时告诉你目录/usr/foo不存在。
有写这段说明的功夫,恐怕足够修改这个bug了。
无言UNIX:课程设置建议
Date: Fri, 24 Apr 92 12:58:28 PT
X-Virus: 6
From: cj@eno.corp.sgi.com (C J Silverio)
Organization: SGI TechPubs
Newsgroups: talk.bizarre
Subject: UNIX Without Words (无言UNIX)
[在一场关于文档无用论的激烈辩论中,我提出了下面这个建议。我胆子小,所以现在才敢公开,供大家参考。]
UNIX Ohne Worter (不会翻 – me)
我被这里散步的文档无用论观点深深折服了。事实上,我进一步认为文档就是毒品,我对于它的依赖性是人为造成的。在专业人士的帮助下,我想我能够戒掉它。
而且,我的良心告诉我不能再靠贩卖这种毒品为生了。我决定回到数学研究院脱胎换骨,彻底从这个寄生虫一样的职业中脱身。
虽然下面这份文档似乎表明了我中毒有多么深,可我还是觉得下一版SGI中应该把它提供给用户。这不过是暂时之举,以后会把它搞掉的。
这是我的建议:
标题:“无言UNIX”
对象:UNIX新手
简介:提供在没有文档条件下使用UNIX的通用策略。展示在没有文档条件下摸清任何操作系统的通用原则。
内容:
介绍:“无文档”哲学简介
为什么手册是恶魔
为什么man手册是恶魔
为什么你还是应该读这份文档
“这将是你读的最后一份文档!”
第一章:如何猜测可能存在哪些命令
第二章:如何猜测命令名
UNIX的怪异缩略命名法
案例:grep
第三章:如何猜测命令选项
如何破解怪异的使用说明
案例:tar
如何知道什么时候顺序是重要的
案例:fine
第四章:如何知道运行正确:没有消息就是好消息
从错误中恢复
第五章:口头传统:你的朋友
第六章:如何获得和维持一个活生生的UNIX高手
如何喂饱你的高手
如何让高手高兴
提供全部新闻组连接的重要性
为什么你的高手需要最快的计算机
免费可乐:高手的长生不老药
如何保持高手身体健康
高手什么时候睡觉?
第七章:常见疑难:你的高手不理你了
识别愚蠢的问题
如何安全地提出愚蠢问题
第八章:如何承受压力
如何对待失败
注:可能只有6、7章才是真正需要的。是的,这才是正路:我把它称为“UNIX高手驯养指南”。
OK, 再也没有文档了。下回书将带你进入sendmail的美好世界,为什么“使用sendmail的感觉和得了花柳病一样。”?下回分解。
标题: 第八章 csh, pipes和find (part 1)
UNIX演义又开始了,本来这回书要表一表sendmail和花柳病的关系,不过sendmail似乎已经从良了,从良妓女比贞节烈女对我们民族的贡献要大得多,所以不想再找她麻烦了,对妓女发展史和性病斗争史感兴趣的,我们可以私下交流。
作为程序员而不是妓女的你,可能对UNIX的编程环境更感兴趣,所以这一节中介绍一下UNIX Shell的历史。我GPL,你没花钱,所以只能任我摆布,我上什么你就吃什么,不要废话。
GPL的好处在于你不必为自己的工作负责,也不必对用户负责,所以sourseforge上充斥着良莠不齐的自由项目。我希望我的心上人也能理解这一点,这一切的开始并不是为了什么价值、责任、过去或是未来,这一切甚至不是为了现在,这一切只是源于passion。
在大海吐出的每个泡沫中
在上班路上吸入的每一粒尘埃中
在过去岁月的每一次阵痛中
在一次一次睡去和醒来中
在天气预报和新闻联播中
在七月流火和九月授衣中
在七月长生殿七日中
在矢车菊和芙蓉中
在长绳纪日中
在天长地久中
在你身边
在我心里
无须寻求意义
第八章 csh, pipes和find
强有力的工具给强有力的傻瓜
“有些操作系统从没有被好好计划,以至于只好用反刍的噪音来命名它的命令(awk, grep, fsck, norff),我想到这个就反胃。”
—— 无名氏
UNIX所谓的“强大工具”是个骗局。这不过是UNIX为了那些东拼西凑的命令和工具所打的幌子。真正的强大工具不需要用户付出太多努力就能提供强大的功能。任何会使改锥和钻头的人都会用电动改锥和电钻。他们不需要搞懂电学、电机、转矩、电磁学、散热或维护。他们只需要把它通上电,带上安全眼镜,然后打开开关。许多人连安全眼镜也不省了。你在五金商店里找不到有致命缺陷的工具:它们不是根本没能投放市场,就是被诉讼搞得焦头烂额。
UNIX设计者的最初目标是提供简单的工具,然而现在的工具则充满了过分的设计和臃肿的功能。比如ls这个列文件的命令竟然有18个选项,提供从排序到指定显示列数的种种功能,而这些功能如果用其他程序实现会更好些(以前正是这样的)。find命令除了查找文件以外还输出cpio格式的文件(而这个功能其实用UNIX名声狼藉的管道很好地实现)。今天,和UNIX类似的电钻将有20个旋钮,连着不标准的电源线,不匹配3/8英寸和7/8英寸的钻头(而这一点会在手册的BUG一章中说明)。
和五金店里的工具不同,许多UNIX强大工具是有缺陷的(有时对文件是致命的):比如tar的不接受超过100个字符的文件名;又比如UNIX调试器总是垮掉,这还不够,它的core文件将覆盖你自己的core,让你下次可以用调试器去调试调试器在调试调试器中生成的core。
Shell游戏
UNIX的发明人有个伟大的想法: 把命令解析器作为一个用户程序实现。如果用户不喜欢缺省的命令解析器,他可以自己写一个。更重要的是,shell能够进化,这样shell将不断进步,变得越来越强大,灵活和易用,至少理论上是这样。
这真是个伟大的想法,不过弄巧成拙了。功能的逐渐增加带来的是一团糟。因为这些功能没有经过设计,只是在不断演化。和所有编程语言所遭到过的诅咒一样,那些利用这些功能的既存shell脚本成为了shell的最大敌人。只要有新的功能加入shell,就会有人在自己的脚本中使用它,这样这个功能就从此长生不老了。坏主意和臭功能往往怎么也死不掉。
于是,你得到了一个不完整、不兼容的shell的大杂烩 (以下每个shell的描述都来自于他们各自的man pages):
sh 是个命令编程语言,用于执行来自终端或文件的命令。
Jsh 和sh一样,但具有csh风味的工作控制 (job control)
Csh C类型语法的shell
Tcsh emacs编辑口味的csh
Ksh KornShell,你的另一个命令和编程语言
Zsh Z Shell
Bash GUN Bourne-Again Shell (GNU Bourne复出Shell)
五金商店里的螺丝刀和锯子,尽管可能来自3、4个不同的厂商,但操作方法都差不多。典型的UNIX在/bin或/usr/bin下存了成百个程序,它们来自众多自以为是的程序员,有着自己的语法、操作范例、使用规则(这一个可以当成管道,而那一个则操作临时文件),不同的命令行参数习惯,以及不同的限制。拿grep和它的变种fgrep, egrep来说,哪一个是最快的?为什么它们接受的参数都不一样,甚至对正则表达式的理解也不尽相同?为什么不能有一个程序提供所有功能?负责的家伙在哪儿啊?
当把命令之间的种种不同都深深烙在脑海中后,你还不能避免被惊着。
Shell Crash
下面这条消息来自哥伦比亚大学编译原理课程的BBS。
Subject: Relevant UNIX bug
October 11, 1991
W4115x课程的同学们:
我们刚学习了活动记录(activation record),参数传递(argument passing)和函数调用规则(calling conventions),你们是否知道下面的输入将让任何一个cshell立刻崩溃?
:!xxx%s%s%s%s%s%s%s%s
你们知道为什么么?
以下的问题供你们思考:
Shell遇到 “!xxx”会做什么?
Shell遇到 “!xxx%s%s%s%s%s%s%s%s”会做什么?
为什么cshell会崩溃?
你将如何修改有关代码来解决这个问题?
最重要的一点:
当你(是的,就是你)将这个前途远大的操作系统用21个字符治服的时候,你觉得天理能容么?
你可以自己试一试。根据UNIX的设计,如果shell垮掉了,你的所有进程将被杀死,你也会被踢出系统。其他操作系统在遇到非法内存访问错误时会弹出调试器,但不是UNIX。
可能这就是为什么UNIX shells不让你在shell的地址空间里动态加载自己的模块,或者直接调用其他程序中的函数。如果这样就太危险了。一步走错,唉哟,你已经被踢出门外了。愚蠢的用户是应该被惩罚的,程序员的错误更是不可容忍。