我的外部記憶區

2006年5月8日星期一

Unix痛恨者手冊:第二章:歡迎新用戶

歡迎新用戶如同用一把上了六顆子彈的左輪槍玩俄羅斯輪盤賭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比其他操作系統更需要提供恢復刪除功能,原因是:

  1. UNIX文件系統沒有版本功能 - 自動的版本維護能保留文件的歷史版本,防止新版本沖掉老版本。UNIX程序員在錯誤處理方面臭名昭著許多程序不檢查是否所有內容都被寫入了磁盤,或被寫入的文件是否存在。有些程序總是刪除輸入文件。
  2. UNIX shell擴展「*」,而不是其子命令 -於是rm這樣的命令就無法檢查「*」這些危險的參數。即使是DOS也對」del *.*」有些提示。但在UNIX下,rm * 和 rm file1 file2…是沒有區別的。
  3. 刪除是永久的 -UNIX沒有undelete命令。許多其他更安全的系統則只是標記被刪除文件所用的塊為「可被使用」,然後把它移到一個特殊目錄下。如果磁盤滿了,這些文件塊才會被重新使用。這一技術不是什麼火箭科學,Macintosh在1984年就提出了「回收站」的想法,而Tenex早在1974年就採用了這一技術。連DOS也提供了簡單的undelete功能,雖然並不總有效。

這四個問題互相合作,製造了無數無法恢復的重要文件。解決的方法早就存在,但UNIX「標準」版中卻從來沒有提供。

歡迎來到未來世界。

「rm」就是終結

許多實際的恐怖故事說明了以上的這些原則。以下是alt.folklore.computers新聞組上流傳的一系列故事中的一個:

Date: Wed, 10 Jan 90X-Virus: 6From: djones@megatest.uucp (Dave Jones)Subject: rm *Newsgroups: alt.folklore.computers

是否有人曾想執行以下命令:

% rm *.o

結果卻打成了:

% rm *>o

現在你得到了一個空文件o,以及大量的空間來存放它!

事實上,你可能連o也得不到,因為shell的文檔並沒有說o是在*被擴展前還是被擴展後被建立的。


上回書說到如何用rm獲得一個空文件和很大的磁盤空間,下面是另一種用法:

Date: Wed, 10 Jan 90X-Virus: 6From: ram@attcan.uucpSubject: Re: rm *Newsgroups: alt.folklore.computers

我也被rm搞過。有一次我想刪除一些/usr/foo/下的東西,我在/usr/foo下敲了以下命令:

% rm –r ./etc% rm –r ./adm

當我要刪除./bin目錄時,我忘敲了那個點。我的系統似乎不太喜歡這個。


當受了這一致命一擊後,UNIX就徹底完蛋了。聰明的系統會給用戶一個恢復的機會(或至少提醒用戶這一操作會導致系統崩潰)。

UNIX迷認為偶爾的文件誤刪除是正常的。比如,可以參考以下下面這個comp.unix.questions上的FAQ:

如何反刪除一個文件?

也許有一天,你不小心執行了一下這個命令:

% rm * .foo

然後發現你把「*」刪掉了。你應該把這當作人生的一課。

當然稱職的系統管理員應該定時備份系統。所以你最好問問他們手中是不是有你的文件備份。

「人生的一課」?沒有任何一個其他廠商用這樣的態度對待一個有缺陷的產品。「大人,我知道您的油箱炸了,但這是人生的一課。」「陪審團的先生女士們,我們將證明電鋸保險開關的失效不過是給用戶上的人生的一課。」不錯。

改變rm的行為也不是個辦法

被rm咬了幾次後,往往會想到用」rm -i」替換rm,或整個替換掉rm,把所有被刪除的文件放到~/.deleted目錄中。這些小技巧讓用戶有了錯誤的安全感。

Date: Mon,16 Apr 90 18:46:33 199X-Virus: 6From: Phil Agre To: UNIX-HATERSSubject: deletion

在我們的系統上,「rm」並不真正刪除文件,而是給文件換了名,這樣」undelete」(不是unrm)這樣的工具就能恢復被刪的文件。

這個功能讓我不再對刪除文件多加小心,反正刪掉了也能找回來。可是,我錯了。Emacs中的刪除並不支持這個功能,Dired命令也是如此。這當然是因為文件恢復並不是操作系統的一個功能。

所以,現在我腦子裡有兩個概念,一個是」deleting」一個文件,一個是」rm』ing」一個文件。當我的手要我的腦子刪除一個文件時,我總要把這兩個概念區分一遍。

一些UNIX專家由此得出了荒謬的結論,他們認為最好別把rm搞得更友好。他們爭辯說,讓UNIX更友好的努力往往適得其反。不幸的是,他們是對的。


Date: Thu, 11 Jan 90 17:17 CSTX-Virus: 6From: 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上有過一次對系統管理員的調查,讓他們說出最恐怖的系統管理故事。72小時內,就有了300多條回應。許多和我們上面描述的文件刪除有關。可笑的是,這些可是UNIX高手。然而正是他們在對「UNIX對用戶不友好」這類指責進行著辯護。

對用戶不友好?UNIX對系統管理員又友好過麼?請看

Date: Wed, 14 Sep 88 01:39 EDTX-Virus: 6From: Matthew P Wiener To: RISKS-LIST@kl.sri.comSubject: 「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 GMTX-Virus: 6From: tk@dcs.ed.ac.uk (Tommy Kelly)Subject: HELP!Newsgroups: cs.questionsOrganization: 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 - 0700X-Virus: 6From: Daniel Weise To: UNIX-HATERSSubject: 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 PDTX-Virus: 6From: Pavel Curtis To: UNIX-HATERSSubject: 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-ethicsrm: messe-ethics nonexistent

% ar m Godar: 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 loveMake: Don』t know how to make love. Stop.

% sleep with mebad 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 senseno sense in pretending

$ drink mattermatter: 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 ESTX-Virus: 6From: David Chapman To: UNIX-HATERSSubject: 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 11: not owner

正確的做法是使用進程號,比如18517。你能用「ps」命令獲得它。當找到了相應進程號後,你只要:

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

注意到UNIX在你的任務被真正殺掉之前就給了你提示符。這又體現了一個UNIX設計原則:

對於給用戶的反饋,能少說絕不多說,能晚說絕不早說。以免過多信息所可能導致的用戶腦損傷。

我希望這些體會能對大家有用。在這一學習過程中,我自己當然被UNIX設計哲學所深深吸引了。我們都應該從UNIX kill命令的雅致、強大和簡潔中學到些東西。

第二章就這麼完了,經歷了這麼多艱難困苦的你已經不是新手了,下回書將介紹UNIX之文檔,或者說UNIX之沒有文檔.

沒有留言: