資料介紹
Linux 和其他類 UNIX 系統(tǒng)總是附帶了大量的工具,它們執(zhí)行從顯而易見的到不可思議的廣泛功能。類 UNIX 編程環(huán)境的成功很大程度上歸功于工具的高品質(zhì)和選擇,以及這些工具之間相互銜接的簡(jiǎn)易性。
作為開發(fā)人員,您可能會(huì)發(fā)現(xiàn)現(xiàn)有實(shí)用程序并不總是能夠解決問題。雖然能夠通過結(jié)合使用現(xiàn)有實(shí)用程序來容易地解決許多問題,然而解決其他問題卻至少需要一些實(shí)際的編程工作。這些后面的任務(wù)通常是創(chuàng)建新實(shí)用程序的候選任務(wù),結(jié)合現(xiàn)有實(shí)用程序來創(chuàng)建新實(shí)用程序可以通過做最少的工作來解決問題。本文考察優(yōu)秀實(shí)用程序所具有的品質(zhì),以及設(shè)計(jì)這種實(shí)用程序所經(jīng)歷的過程。
優(yōu)秀的實(shí)用程序具有哪些品質(zhì)?
Kernighan & Pike 所著的 The UNIX Programming Environment 一書中包含了對(duì)此問題的精彩討論。優(yōu)秀的實(shí)用程序是把自己的工作做得盡可能好的實(shí)用程序。它必須與其他實(shí)用程序配合融洽;必須能夠容易地與其他實(shí)用程序結(jié)合使用。無法與其他實(shí)用程序結(jié)合使用的程序不是實(shí)用程序,而是應(yīng)用程序。
實(shí)用程序應(yīng)該允許您根據(jù)手邊的材料廉價(jià)而容易地構(gòu)建一次性的應(yīng)用程序。許多人認(rèn)為實(shí)用程序就像是工具箱中的工具。設(shè)計(jì)實(shí)用程序的目標(biāo)不是為了讓單個(gè)工具來做所有事情,而是為了擁有一組工具,其中每個(gè)工具都盡可能好地做一件事情。
有些實(shí)用程序自身就是相當(dāng)有用的,而其他實(shí)用程序則必須與一系列實(shí)用程序配合使用。前者的例子包括 sort 和 grep 。另一方面, xargs 除了與其他實(shí)用程序(最常見的是 find )配合使用外,很少單獨(dú)使用。
使用什么語(yǔ)言來編寫實(shí)用程序?
大多數(shù) UNIX 系統(tǒng)實(shí)用程序都是用 C 語(yǔ)言來編寫的。本文中的例子使用 Perl 和 sh。應(yīng)該使用恰當(dāng)?shù)墓ぞ邅碜銮‘?dāng)?shù)氖虑?。如果您?duì)某個(gè)實(shí)用程序使用得足夠頻繁,那么用編譯型語(yǔ)言來編寫它的成本也許能通過性能提升來獲得回報(bào)。另一方面,對(duì)于程序的工作負(fù)荷很輕這種相當(dāng)普遍的情況,使用腳本語(yǔ)言也許會(huì)提供更快的開發(fā)速度。
如果無法肯定,您應(yīng)該使用自己最了解的語(yǔ)言。至少當(dāng)您在對(duì)某個(gè)實(shí)用程序進(jìn)行原型化,或在弄清它是如何有用時(shí),程序員效率將優(yōu)先于性能調(diào)整。大多數(shù) UNIX 系統(tǒng)實(shí)用程序都是用 C 編寫的,這只是因?yàn)檫@些實(shí)用程序使用得足夠頻繁,以致考慮效率比考慮開發(fā)成本更加重要。Perl 和 sh(或 ksh)可能是用于快速原型化的很好語(yǔ)言。對(duì)于與其他程序配合實(shí)用的實(shí)用程序,使用 shell 來編寫它們或許要比使用更傳統(tǒng)的編程語(yǔ)言來編寫它們要容易一些。另一方面,當(dāng)您希望與原始的字節(jié)交互時(shí),C 或許就是最好的選擇。
設(shè)計(jì)實(shí)用程序
一個(gè)不錯(cuò)的經(jīng)驗(yàn)法則就是當(dāng)您第二次必須解決某個(gè)問題時(shí),首先考慮實(shí)用程序的設(shè)計(jì)。不要對(duì)第一次編寫的一次性作品感到遺憾;您可以將它看作是一個(gè)原型。第二次,請(qǐng)把您所需的功能與第一次所需的功能作比較。在第三次前后,您應(yīng)該開始考慮花時(shí)間來編寫一個(gè)通用實(shí)用程序。即使純粹的重復(fù)性任務(wù)也可能會(huì)給實(shí)用程序的開發(fā)帶來好處;例如,由于人們對(duì)嘗試以通用的方式重命名文件感到失望,于是開發(fā)了許多通用文件重命名程序。
下面是一些實(shí)用程序設(shè)計(jì)目標(biāo);每個(gè)目標(biāo)將在下面單獨(dú)的小節(jié)中介紹。
做好一件事情。
成為一個(gè)過濾器。
通用化。
健壯。
新穎。
做好一件事情
做好一件事情;不要糟糕地做多件事情。關(guān)于做好一件事情的最佳例子或許是 sort 。除了 sort 外,沒有 其他哪個(gè)實(shí)用程序具有排序功能?;镜乃枷牒芎?jiǎn)單:如果一次僅解決一個(gè)問題,您就能花時(shí)間把它解決好。
設(shè)想一下,如果大多數(shù)程序都具有排序功能,但是有些僅支持按詞法排序,而其他一些僅支持按數(shù)字排序,另外一些甚至支持關(guān)鍵字選擇而不是對(duì)整行排序,那將是一件多么令人沮喪的事情。起碼,這也是惱人的。
當(dāng)您發(fā)現(xiàn)某個(gè)問題需要解決時(shí),應(yīng)嘗試將問題分解為多個(gè)部分,不要重復(fù)那些其他實(shí)用程序中已經(jīng)存在的部分。您對(duì)允許配合現(xiàn)有工具使用的工具關(guān)注得越多,您的實(shí)用程序就越有可能保持有用。
也許您需要編寫多個(gè)程序。完成專門任務(wù)的最佳途徑通常是編寫一兩個(gè)實(shí)用程序,再用一些線索將它們聯(lián)系起來,而不是編寫單個(gè)程序來解決整件事情。使用 20 行的 shell 腳本來將新的實(shí)用程序與現(xiàn)有工具結(jié)合起來是很理想的。如果嘗試一次解決整個(gè)問題,隨之而來的第一個(gè)變更就可能要求您全盤重新考慮。
我偶爾需要從數(shù)據(jù)庫(kù)生成兩列或三列的輸出。編寫一個(gè)程序在單個(gè)列中生成輸出,然后結(jié)合使用一個(gè)對(duì)輸出進(jìn)行分列的程序,這樣通常會(huì)更有效率。組合這兩個(gè)實(shí)用程序的 shell 腳本本身是臨時(shí)性的,單獨(dú)的實(shí)用程序比這個(gè)腳本的使用壽命更長(zhǎng)。
有些實(shí)用程序服務(wù)于非常專一的需要。針對(duì)一個(gè)包含大量?jī)?nèi)容的目錄,如果 ls 的輸出非??斓貪L出屏幕,這可能是因?yàn)槠渲杏幸粋€(gè)文件具有非常長(zhǎng)的文件名,從而迫使 ls 僅對(duì)輸出使用單個(gè)列。使用 more 來對(duì)輸出分頁(yè)會(huì)花一些時(shí)間。為什么不像下面這樣就按長(zhǎng)度對(duì)行排序,然后通過 tail 來管道輸出結(jié)果呢?
清單 1. 世間能找到的最小實(shí)用程序 sl
#/usr/bin/perl -w
print sort { length $a 《=》 length $b } 《》;
清單 1 中的腳本確切地就做一件事情。它不接受任何選項(xiàng),因?yàn)樗恍枰x項(xiàng);它僅關(guān)心行的長(zhǎng)度。歸功于 Perl 便利的 《》 表達(dá)方式,這個(gè)小實(shí)用程序既適用于標(biāo)準(zhǔn)輸入,也適用于命令行指定的文件。
成為一個(gè)過濾器
幾乎所有實(shí)用程序都最適合想像為過濾器,盡管有一些非常有用的實(shí)用程序不符合這個(gè)模型。(例如,某個(gè)程序在執(zhí)行計(jì)數(shù)時(shí)可能非常有用,盡管它作為過濾器工作得并不好。僅接受命令行參數(shù)作為輸入并潛在地產(chǎn)生復(fù)雜輸出的程序可能非常有用。)然而,大多數(shù)實(shí)用程序都應(yīng)該作為過濾器來工作。根據(jù)慣例,過濾器對(duì)文本的行起作用。大多數(shù)過濾器都應(yīng)該支持多個(gè)輸入文件。
記住實(shí)用程序需要在命令行和腳本中運(yùn)行。有時(shí),理想的行為會(huì)稍有不同。例如,大多數(shù)版本的 ls 都會(huì)在向終端寫出時(shí)自動(dòng)將輸入排序到多個(gè)列中。 grep 的默認(rèn)行為是在指定多個(gè)文件的情況下打印從其中找到匹配項(xiàng)的那個(gè)文件名稱。這樣的差別應(yīng)該與用戶希望的實(shí)用程序工作方式有關(guān),而不是與其他事項(xiàng)有關(guān)。例如,舊版本的 GNU bc 在啟動(dòng)時(shí)顯示強(qiáng)迫性的版權(quán)標(biāo)記。請(qǐng)不要那樣做。讓您的實(shí)用程序僅做它應(yīng)該做的事情。
實(shí)用程序喜歡生活在管道中。管道允許實(shí)用程序?qū)W⒂谧约旱墓ぷ?,而不是去關(guān)注旁枝末節(jié)。為了生活在管道中,實(shí)用程序需要從標(biāo)準(zhǔn)輸入讀取數(shù)據(jù),然后向標(biāo)準(zhǔn)輸出寫出數(shù)據(jù)。如果您希望處理記錄,那么您最好能夠使每一行成為一個(gè)“記錄”。諸如 sort 和 join 之類的現(xiàn)有程序已經(jīng)在那樣考慮了。它們將會(huì)因?yàn)槟@樣做而感謝您。
我偶爾使用這樣一個(gè)實(shí)用程序,它針對(duì)一個(gè)文件樹反復(fù)調(diào)用其他程序。這充分利用了標(biāo)準(zhǔn)的 UNIX 實(shí)用程序過濾器模型,但是該模型僅適用于讀取輸入然后寫出輸出的實(shí)用程序;不能將它用于就地操作或接受輸入輸出文件名的實(shí)用程序。
可以使用標(biāo)準(zhǔn)輸入來運(yùn)行的大多數(shù)程序也完全可以針對(duì)單個(gè)文件或一組文件運(yùn)行。注意,可以證明這樣違背了反對(duì)重復(fù)工作的規(guī)則;顯而易見,這可以通過將 cat 的輸出饋送給該系列中的下一個(gè)程序來解決。然而這在實(shí)踐中似乎是合理的。
有些程序可能合法地讀取一種格式的記錄,但是卻產(chǎn)生完全不同的輸出。這樣的一個(gè)例子就是將輸入材料劃分為列的實(shí)用程序。這樣一個(gè)實(shí)用程序可能將輸入中的行視為記錄,但是卻在輸出中的每行上產(chǎn)生多個(gè)記錄。
并非每個(gè)實(shí)用程序都完全符合這個(gè)模型。例如, xargs 不是接受記錄而是接受文件名作為輸入,并且所有的實(shí)際處理都是由其他程序完成的。
通用化
嘗試將任務(wù)看作與您實(shí)際執(zhí)行的任務(wù)類似;如果您能找出這些任務(wù)的通用描述,那么最好嘗試編寫一個(gè)符合該描述的實(shí)用程序。例如,如果您發(fā)現(xiàn)自己一天在根據(jù)詞法對(duì)文本排序,而另一天在根據(jù)數(shù)字對(duì)文本排序,那么考慮編寫一個(gè)通用排序?qū)嵱贸绦蛞苍S是有意義的。
對(duì)功能進(jìn)行通用化有時(shí)會(huì)導(dǎo)致您發(fā)現(xiàn):某個(gè)看起來似乎像單個(gè)實(shí)用程序的程序,實(shí)際上卻是配合起來使用的兩個(gè)實(shí)用程序。這很好。編寫兩個(gè)設(shè)計(jì)良好的實(shí)用程序可能要比編寫一個(gè)丑陋的或復(fù)雜的實(shí)用程序更容易。
做好一件事情并不意味著 僅僅做一件事情。它意味著處理一致但有用的問題空間。許多人都使用 grep 。然而,它的大量效用在于執(zhí)行相關(guān)任務(wù)的能力。 grep 的各種選項(xiàng)完成許多小實(shí)用程序的工作,如果這些工作都由單獨(dú)的小實(shí)用程序來完成,最終會(huì)造成大量共享的、重復(fù)的代碼。
這條規(guī)則,以及做好一件事情的規(guī)則,都是一個(gè)根本原理的必然結(jié)果:無論何時(shí)都要盡可能避免代碼重復(fù)。如果您編寫半打程序,其中每個(gè)都對(duì)行排序,您最終可能必須六次修復(fù)六個(gè)類似的 bug,而不是去使用一個(gè)得到更好維護(hù)的 sort 程序。
這是編寫實(shí)用程序的一部分,即把大多數(shù)工作添加到完成該實(shí)用程序的過程中。您也許沒有時(shí)間在最初就完全通用化一個(gè)實(shí)用程序,但是當(dāng)您一直使用該實(shí)用程序就會(huì)獲得相應(yīng)的回報(bào)。
有時(shí),向某個(gè)程序添加相關(guān)功能是很有用的,即使這個(gè)功能并不是用來完成完全相同的任務(wù)。例如,當(dāng)運(yùn)行在終端設(shè)備上時(shí),對(duì)原始二進(jìn)制數(shù)據(jù)進(jìn)行完美打印的程序可能更為有用,因?yàn)樗菇K端進(jìn)入原始模式。這樣使得測(cè)試涉及鍵盤映射、新鍵盤等的問題變得容易多了。不確定為什么當(dāng)您按 delete 鍵時(shí)卻得到代字號(hào)(~)嗎? 這是弄清實(shí)際發(fā)送了什么內(nèi)容的容易途徑。這并不是完全相同的任務(wù),但它足夠類似,因而可能成為一個(gè)附加特性。
清單 2 中的 errno 實(shí)用程序就是通用化的很好例子,因?yàn)樗瑫r(shí)支持?jǐn)?shù)字和符號(hào)名稱。
健壯
實(shí)用程序的穩(wěn)定性是很重要的。容易崩潰或無法處理真實(shí)數(shù)據(jù)的實(shí)用程序不是有用的實(shí)用程序。實(shí)用程序應(yīng)該能夠處理任意長(zhǎng)度的行、巨型文件,等等。實(shí)用程序無法處理超過其內(nèi)存容量的數(shù)據(jù)集或許是可以容忍的,但是有些實(shí)用程序不是這樣;例如, sort 通過使用臨時(shí)文件,一般能夠?qū)Ρ绕鋬?nèi)存容量大得多的數(shù)據(jù)集排序。
應(yīng)該盡量確保弄清楚您的實(shí)用程序可能要操作哪些數(shù)據(jù)。不要簡(jiǎn)單地忽略無法處理的數(shù)據(jù)的可能性。應(yīng)該檢查這種情況并診斷您的實(shí)用程序。錯(cuò)誤消息越明確,您對(duì)用戶就越有幫助。盡量給用戶提供足夠的信息,以便讓他們知道發(fā)生了什么情況以及如何解決。當(dāng)處理數(shù)據(jù)文件時(shí),盡量準(zhǔn)確識(shí)別出不良的數(shù)據(jù)。當(dāng)嘗試解析數(shù)字時(shí),不要簡(jiǎn)單地放棄;應(yīng)該告訴用戶您得到了什么數(shù)據(jù),而且如果可能的話,還應(yīng)該告訴用戶該數(shù)據(jù)位于輸入流中的哪一行上。
作為一個(gè)很好的例子,請(qǐng)考慮 dc 的兩種實(shí)現(xiàn)之間的區(qū)別。如果您運(yùn)行 dc /home ,其中一種實(shí)現(xiàn)會(huì)顯示“Cannot use directory as input!”而另一種實(shí)現(xiàn)只是無聲地返回,沒有錯(cuò)誤消息,也沒有不尋常的退出代碼。當(dāng)您錯(cuò)誤地鍵入一個(gè) cd 命令時(shí),您更希望當(dāng)前路徑中有哪一種實(shí)現(xiàn)呢?類似地,如果您提供某個(gè)目錄中的數(shù)據(jù)流(或許是執(zhí)行 dc 《 /home ),前者會(huì)給出詳細(xì)的錯(cuò)誤消息。另一方面,當(dāng)它在獲得無效數(shù)據(jù)的早期就選擇放棄可能是理想的。
安全漏洞經(jīng)常植根于在意料之外的數(shù)據(jù)面前表現(xiàn)得不夠健壯的程序中。務(wù)必記住,優(yōu)秀的實(shí)用程序能夠設(shè)法在 shell 腳本中作為根(root)用戶身份運(yùn)行。諸如 find 這樣的程序中的緩沖區(qū)溢出可能會(huì)給大量的系統(tǒng)帶來風(fēng)險(xiǎn)。
程序?qū)σ饬现獾臄?shù)據(jù)處理得越好,它就更可能適應(yīng)變化的環(huán)境。通常,設(shè)法使程序更健壯會(huì)導(dǎo)致您更好地理解該程序的作用,從而更好地使之通用化。
新穎
要編寫的最糟糕的實(shí)用程序種類之一就是您已經(jīng)有了的實(shí)用程序。我編寫過一個(gè)名為 count 的美妙的實(shí)用程序。它允許我執(zhí)行幾乎任何計(jì)數(shù)任務(wù)。它是一個(gè)出色的實(shí)用程序,但是已經(jīng)有一個(gè)名為 jot 的標(biāo)準(zhǔn) BSD 實(shí)用程序做同樣的事情。同樣地,我的一個(gè)用于將數(shù)據(jù)轉(zhuǎn)換為列的靈活的程序重復(fù)了一個(gè)現(xiàn)有實(shí)用程序 rs 的功能,這個(gè)實(shí)用程序同樣可以在 BSD 系統(tǒng)上找到,只不過 rs 更靈活,設(shè)計(jì)得更好。請(qǐng)參閱下面的 參考資料 以了解關(guān)于 jot 和 rs 的更多信息。
作為開發(fā)人員,您可能會(huì)發(fā)現(xiàn)現(xiàn)有實(shí)用程序并不總是能夠解決問題。雖然能夠通過結(jié)合使用現(xiàn)有實(shí)用程序來容易地解決許多問題,然而解決其他問題卻至少需要一些實(shí)際的編程工作。這些后面的任務(wù)通常是創(chuàng)建新實(shí)用程序的候選任務(wù),結(jié)合現(xiàn)有實(shí)用程序來創(chuàng)建新實(shí)用程序可以通過做最少的工作來解決問題。本文考察優(yōu)秀實(shí)用程序所具有的品質(zhì),以及設(shè)計(jì)這種實(shí)用程序所經(jīng)歷的過程。
優(yōu)秀的實(shí)用程序具有哪些品質(zhì)?
Kernighan & Pike 所著的 The UNIX Programming Environment 一書中包含了對(duì)此問題的精彩討論。優(yōu)秀的實(shí)用程序是把自己的工作做得盡可能好的實(shí)用程序。它必須與其他實(shí)用程序配合融洽;必須能夠容易地與其他實(shí)用程序結(jié)合使用。無法與其他實(shí)用程序結(jié)合使用的程序不是實(shí)用程序,而是應(yīng)用程序。
實(shí)用程序應(yīng)該允許您根據(jù)手邊的材料廉價(jià)而容易地構(gòu)建一次性的應(yīng)用程序。許多人認(rèn)為實(shí)用程序就像是工具箱中的工具。設(shè)計(jì)實(shí)用程序的目標(biāo)不是為了讓單個(gè)工具來做所有事情,而是為了擁有一組工具,其中每個(gè)工具都盡可能好地做一件事情。
有些實(shí)用程序自身就是相當(dāng)有用的,而其他實(shí)用程序則必須與一系列實(shí)用程序配合使用。前者的例子包括 sort 和 grep 。另一方面, xargs 除了與其他實(shí)用程序(最常見的是 find )配合使用外,很少單獨(dú)使用。
使用什么語(yǔ)言來編寫實(shí)用程序?
大多數(shù) UNIX 系統(tǒng)實(shí)用程序都是用 C 語(yǔ)言來編寫的。本文中的例子使用 Perl 和 sh。應(yīng)該使用恰當(dāng)?shù)墓ぞ邅碜銮‘?dāng)?shù)氖虑?。如果您?duì)某個(gè)實(shí)用程序使用得足夠頻繁,那么用編譯型語(yǔ)言來編寫它的成本也許能通過性能提升來獲得回報(bào)。另一方面,對(duì)于程序的工作負(fù)荷很輕這種相當(dāng)普遍的情況,使用腳本語(yǔ)言也許會(huì)提供更快的開發(fā)速度。
如果無法肯定,您應(yīng)該使用自己最了解的語(yǔ)言。至少當(dāng)您在對(duì)某個(gè)實(shí)用程序進(jìn)行原型化,或在弄清它是如何有用時(shí),程序員效率將優(yōu)先于性能調(diào)整。大多數(shù) UNIX 系統(tǒng)實(shí)用程序都是用 C 編寫的,這只是因?yàn)檫@些實(shí)用程序使用得足夠頻繁,以致考慮效率比考慮開發(fā)成本更加重要。Perl 和 sh(或 ksh)可能是用于快速原型化的很好語(yǔ)言。對(duì)于與其他程序配合實(shí)用的實(shí)用程序,使用 shell 來編寫它們或許要比使用更傳統(tǒng)的編程語(yǔ)言來編寫它們要容易一些。另一方面,當(dāng)您希望與原始的字節(jié)交互時(shí),C 或許就是最好的選擇。
設(shè)計(jì)實(shí)用程序
一個(gè)不錯(cuò)的經(jīng)驗(yàn)法則就是當(dāng)您第二次必須解決某個(gè)問題時(shí),首先考慮實(shí)用程序的設(shè)計(jì)。不要對(duì)第一次編寫的一次性作品感到遺憾;您可以將它看作是一個(gè)原型。第二次,請(qǐng)把您所需的功能與第一次所需的功能作比較。在第三次前后,您應(yīng)該開始考慮花時(shí)間來編寫一個(gè)通用實(shí)用程序。即使純粹的重復(fù)性任務(wù)也可能會(huì)給實(shí)用程序的開發(fā)帶來好處;例如,由于人們對(duì)嘗試以通用的方式重命名文件感到失望,于是開發(fā)了許多通用文件重命名程序。
下面是一些實(shí)用程序設(shè)計(jì)目標(biāo);每個(gè)目標(biāo)將在下面單獨(dú)的小節(jié)中介紹。
做好一件事情。
成為一個(gè)過濾器。
通用化。
健壯。
新穎。
做好一件事情
做好一件事情;不要糟糕地做多件事情。關(guān)于做好一件事情的最佳例子或許是 sort 。除了 sort 外,沒有 其他哪個(gè)實(shí)用程序具有排序功能?;镜乃枷牒芎?jiǎn)單:如果一次僅解決一個(gè)問題,您就能花時(shí)間把它解決好。
設(shè)想一下,如果大多數(shù)程序都具有排序功能,但是有些僅支持按詞法排序,而其他一些僅支持按數(shù)字排序,另外一些甚至支持關(guān)鍵字選擇而不是對(duì)整行排序,那將是一件多么令人沮喪的事情。起碼,這也是惱人的。
當(dāng)您發(fā)現(xiàn)某個(gè)問題需要解決時(shí),應(yīng)嘗試將問題分解為多個(gè)部分,不要重復(fù)那些其他實(shí)用程序中已經(jīng)存在的部分。您對(duì)允許配合現(xiàn)有工具使用的工具關(guān)注得越多,您的實(shí)用程序就越有可能保持有用。
也許您需要編寫多個(gè)程序。完成專門任務(wù)的最佳途徑通常是編寫一兩個(gè)實(shí)用程序,再用一些線索將它們聯(lián)系起來,而不是編寫單個(gè)程序來解決整件事情。使用 20 行的 shell 腳本來將新的實(shí)用程序與現(xiàn)有工具結(jié)合起來是很理想的。如果嘗試一次解決整個(gè)問題,隨之而來的第一個(gè)變更就可能要求您全盤重新考慮。
我偶爾需要從數(shù)據(jù)庫(kù)生成兩列或三列的輸出。編寫一個(gè)程序在單個(gè)列中生成輸出,然后結(jié)合使用一個(gè)對(duì)輸出進(jìn)行分列的程序,這樣通常會(huì)更有效率。組合這兩個(gè)實(shí)用程序的 shell 腳本本身是臨時(shí)性的,單獨(dú)的實(shí)用程序比這個(gè)腳本的使用壽命更長(zhǎng)。
有些實(shí)用程序服務(wù)于非常專一的需要。針對(duì)一個(gè)包含大量?jī)?nèi)容的目錄,如果 ls 的輸出非??斓貪L出屏幕,這可能是因?yàn)槠渲杏幸粋€(gè)文件具有非常長(zhǎng)的文件名,從而迫使 ls 僅對(duì)輸出使用單個(gè)列。使用 more 來對(duì)輸出分頁(yè)會(huì)花一些時(shí)間。為什么不像下面這樣就按長(zhǎng)度對(duì)行排序,然后通過 tail 來管道輸出結(jié)果呢?
清單 1. 世間能找到的最小實(shí)用程序 sl
#/usr/bin/perl -w
print sort { length $a 《=》 length $b } 《》;
清單 1 中的腳本確切地就做一件事情。它不接受任何選項(xiàng),因?yàn)樗恍枰x項(xiàng);它僅關(guān)心行的長(zhǎng)度。歸功于 Perl 便利的 《》 表達(dá)方式,這個(gè)小實(shí)用程序既適用于標(biāo)準(zhǔn)輸入,也適用于命令行指定的文件。
成為一個(gè)過濾器
幾乎所有實(shí)用程序都最適合想像為過濾器,盡管有一些非常有用的實(shí)用程序不符合這個(gè)模型。(例如,某個(gè)程序在執(zhí)行計(jì)數(shù)時(shí)可能非常有用,盡管它作為過濾器工作得并不好。僅接受命令行參數(shù)作為輸入并潛在地產(chǎn)生復(fù)雜輸出的程序可能非常有用。)然而,大多數(shù)實(shí)用程序都應(yīng)該作為過濾器來工作。根據(jù)慣例,過濾器對(duì)文本的行起作用。大多數(shù)過濾器都應(yīng)該支持多個(gè)輸入文件。
記住實(shí)用程序需要在命令行和腳本中運(yùn)行。有時(shí),理想的行為會(huì)稍有不同。例如,大多數(shù)版本的 ls 都會(huì)在向終端寫出時(shí)自動(dòng)將輸入排序到多個(gè)列中。 grep 的默認(rèn)行為是在指定多個(gè)文件的情況下打印從其中找到匹配項(xiàng)的那個(gè)文件名稱。這樣的差別應(yīng)該與用戶希望的實(shí)用程序工作方式有關(guān),而不是與其他事項(xiàng)有關(guān)。例如,舊版本的 GNU bc 在啟動(dòng)時(shí)顯示強(qiáng)迫性的版權(quán)標(biāo)記。請(qǐng)不要那樣做。讓您的實(shí)用程序僅做它應(yīng)該做的事情。
實(shí)用程序喜歡生活在管道中。管道允許實(shí)用程序?qū)W⒂谧约旱墓ぷ?,而不是去關(guān)注旁枝末節(jié)。為了生活在管道中,實(shí)用程序需要從標(biāo)準(zhǔn)輸入讀取數(shù)據(jù),然后向標(biāo)準(zhǔn)輸出寫出數(shù)據(jù)。如果您希望處理記錄,那么您最好能夠使每一行成為一個(gè)“記錄”。諸如 sort 和 join 之類的現(xiàn)有程序已經(jīng)在那樣考慮了。它們將會(huì)因?yàn)槟@樣做而感謝您。
我偶爾使用這樣一個(gè)實(shí)用程序,它針對(duì)一個(gè)文件樹反復(fù)調(diào)用其他程序。這充分利用了標(biāo)準(zhǔn)的 UNIX 實(shí)用程序過濾器模型,但是該模型僅適用于讀取輸入然后寫出輸出的實(shí)用程序;不能將它用于就地操作或接受輸入輸出文件名的實(shí)用程序。
可以使用標(biāo)準(zhǔn)輸入來運(yùn)行的大多數(shù)程序也完全可以針對(duì)單個(gè)文件或一組文件運(yùn)行。注意,可以證明這樣違背了反對(duì)重復(fù)工作的規(guī)則;顯而易見,這可以通過將 cat 的輸出饋送給該系列中的下一個(gè)程序來解決。然而這在實(shí)踐中似乎是合理的。
有些程序可能合法地讀取一種格式的記錄,但是卻產(chǎn)生完全不同的輸出。這樣的一個(gè)例子就是將輸入材料劃分為列的實(shí)用程序。這樣一個(gè)實(shí)用程序可能將輸入中的行視為記錄,但是卻在輸出中的每行上產(chǎn)生多個(gè)記錄。
并非每個(gè)實(shí)用程序都完全符合這個(gè)模型。例如, xargs 不是接受記錄而是接受文件名作為輸入,并且所有的實(shí)際處理都是由其他程序完成的。
通用化
嘗試將任務(wù)看作與您實(shí)際執(zhí)行的任務(wù)類似;如果您能找出這些任務(wù)的通用描述,那么最好嘗試編寫一個(gè)符合該描述的實(shí)用程序。例如,如果您發(fā)現(xiàn)自己一天在根據(jù)詞法對(duì)文本排序,而另一天在根據(jù)數(shù)字對(duì)文本排序,那么考慮編寫一個(gè)通用排序?qū)嵱贸绦蛞苍S是有意義的。
對(duì)功能進(jìn)行通用化有時(shí)會(huì)導(dǎo)致您發(fā)現(xiàn):某個(gè)看起來似乎像單個(gè)實(shí)用程序的程序,實(shí)際上卻是配合起來使用的兩個(gè)實(shí)用程序。這很好。編寫兩個(gè)設(shè)計(jì)良好的實(shí)用程序可能要比編寫一個(gè)丑陋的或復(fù)雜的實(shí)用程序更容易。
做好一件事情并不意味著 僅僅做一件事情。它意味著處理一致但有用的問題空間。許多人都使用 grep 。然而,它的大量效用在于執(zhí)行相關(guān)任務(wù)的能力。 grep 的各種選項(xiàng)完成許多小實(shí)用程序的工作,如果這些工作都由單獨(dú)的小實(shí)用程序來完成,最終會(huì)造成大量共享的、重復(fù)的代碼。
這條規(guī)則,以及做好一件事情的規(guī)則,都是一個(gè)根本原理的必然結(jié)果:無論何時(shí)都要盡可能避免代碼重復(fù)。如果您編寫半打程序,其中每個(gè)都對(duì)行排序,您最終可能必須六次修復(fù)六個(gè)類似的 bug,而不是去使用一個(gè)得到更好維護(hù)的 sort 程序。
這是編寫實(shí)用程序的一部分,即把大多數(shù)工作添加到完成該實(shí)用程序的過程中。您也許沒有時(shí)間在最初就完全通用化一個(gè)實(shí)用程序,但是當(dāng)您一直使用該實(shí)用程序就會(huì)獲得相應(yīng)的回報(bào)。
有時(shí),向某個(gè)程序添加相關(guān)功能是很有用的,即使這個(gè)功能并不是用來完成完全相同的任務(wù)。例如,當(dāng)運(yùn)行在終端設(shè)備上時(shí),對(duì)原始二進(jìn)制數(shù)據(jù)進(jìn)行完美打印的程序可能更為有用,因?yàn)樗菇K端進(jìn)入原始模式。這樣使得測(cè)試涉及鍵盤映射、新鍵盤等的問題變得容易多了。不確定為什么當(dāng)您按 delete 鍵時(shí)卻得到代字號(hào)(~)嗎? 這是弄清實(shí)際發(fā)送了什么內(nèi)容的容易途徑。這并不是完全相同的任務(wù),但它足夠類似,因而可能成為一個(gè)附加特性。
清單 2 中的 errno 實(shí)用程序就是通用化的很好例子,因?yàn)樗瑫r(shí)支持?jǐn)?shù)字和符號(hào)名稱。
健壯
實(shí)用程序的穩(wěn)定性是很重要的。容易崩潰或無法處理真實(shí)數(shù)據(jù)的實(shí)用程序不是有用的實(shí)用程序。實(shí)用程序應(yīng)該能夠處理任意長(zhǎng)度的行、巨型文件,等等。實(shí)用程序無法處理超過其內(nèi)存容量的數(shù)據(jù)集或許是可以容忍的,但是有些實(shí)用程序不是這樣;例如, sort 通過使用臨時(shí)文件,一般能夠?qū)Ρ绕鋬?nèi)存容量大得多的數(shù)據(jù)集排序。
應(yīng)該盡量確保弄清楚您的實(shí)用程序可能要操作哪些數(shù)據(jù)。不要簡(jiǎn)單地忽略無法處理的數(shù)據(jù)的可能性。應(yīng)該檢查這種情況并診斷您的實(shí)用程序。錯(cuò)誤消息越明確,您對(duì)用戶就越有幫助。盡量給用戶提供足夠的信息,以便讓他們知道發(fā)生了什么情況以及如何解決。當(dāng)處理數(shù)據(jù)文件時(shí),盡量準(zhǔn)確識(shí)別出不良的數(shù)據(jù)。當(dāng)嘗試解析數(shù)字時(shí),不要簡(jiǎn)單地放棄;應(yīng)該告訴用戶您得到了什么數(shù)據(jù),而且如果可能的話,還應(yīng)該告訴用戶該數(shù)據(jù)位于輸入流中的哪一行上。
作為一個(gè)很好的例子,請(qǐng)考慮 dc 的兩種實(shí)現(xiàn)之間的區(qū)別。如果您運(yùn)行 dc /home ,其中一種實(shí)現(xiàn)會(huì)顯示“Cannot use directory as input!”而另一種實(shí)現(xiàn)只是無聲地返回,沒有錯(cuò)誤消息,也沒有不尋常的退出代碼。當(dāng)您錯(cuò)誤地鍵入一個(gè) cd 命令時(shí),您更希望當(dāng)前路徑中有哪一種實(shí)現(xiàn)呢?類似地,如果您提供某個(gè)目錄中的數(shù)據(jù)流(或許是執(zhí)行 dc 《 /home ),前者會(huì)給出詳細(xì)的錯(cuò)誤消息。另一方面,當(dāng)它在獲得無效數(shù)據(jù)的早期就選擇放棄可能是理想的。
安全漏洞經(jīng)常植根于在意料之外的數(shù)據(jù)面前表現(xiàn)得不夠健壯的程序中。務(wù)必記住,優(yōu)秀的實(shí)用程序能夠設(shè)法在 shell 腳本中作為根(root)用戶身份運(yùn)行。諸如 find 這樣的程序中的緩沖區(qū)溢出可能會(huì)給大量的系統(tǒng)帶來風(fēng)險(xiǎn)。
程序?qū)σ饬现獾臄?shù)據(jù)處理得越好,它就更可能適應(yīng)變化的環(huán)境。通常,設(shè)法使程序更健壯會(huì)導(dǎo)致您更好地理解該程序的作用,從而更好地使之通用化。
新穎
要編寫的最糟糕的實(shí)用程序種類之一就是您已經(jīng)有了的實(shí)用程序。我編寫過一個(gè)名為 count 的美妙的實(shí)用程序。它允許我執(zhí)行幾乎任何計(jì)數(shù)任務(wù)。它是一個(gè)出色的實(shí)用程序,但是已經(jīng)有一個(gè)名為 jot 的標(biāo)準(zhǔn) BSD 實(shí)用程序做同樣的事情。同樣地,我的一個(gè)用于將數(shù)據(jù)轉(zhuǎn)換為列的靈活的程序重復(fù)了一個(gè)現(xiàn)有實(shí)用程序 rs 的功能,這個(gè)實(shí)用程序同樣可以在 BSD 系統(tǒng)上找到,只不過 rs 更靈活,設(shè)計(jì)得更好。請(qǐng)參閱下面的 參考資料 以了解關(guān)于 jot 和 rs 的更多信息。
下載該資料的人也在下載
下載該資料的人還在閱讀
更多 >
- Linux Elxflash 10.2.370.19獨(dú)立的實(shí)用程序
- Linux Elxflash 10.0.803.23獨(dú)立實(shí)用程序
- Elxflash獨(dú)立的實(shí)用程序為Linux-RHEL 7.5
- Elxflash獨(dú)立的Linux實(shí)用程序14.0.505.14
- Linux Elxflash 10.0.878.0獨(dú)立的實(shí)用程序
- Elxflash獨(dú)立的Linux實(shí)用程序 12.6.240.40-1
- Elxflash獨(dú)立的實(shí)用程序為Linux 版本11.0.243.20
- Elxflash獨(dú)立Linux實(shí)用程序
- Elxflash的Linux實(shí)用程序
- Elxflash獨(dú)立的Linux實(shí)用程序分享
- Elxflash-f Linux實(shí)用程序
- Elxflash獨(dú)立的實(shí)用程序為Linux-RHEL 8.0
- Elxflash獨(dú)立的Linux實(shí)用程序
- MTCH6301實(shí)用程序2.04版的詳細(xì)中文資料概述 0次下載
- Linux系統(tǒng)網(wǎng)絡(luò)驅(qū)動(dòng)程序的編寫 0次下載
- 基于OpenHarmony編寫GPIO平臺(tái)驅(qū)動(dòng)和應(yīng)用程序 924次閱讀
- 常用的工具和實(shí)用程序 825次閱讀
- Linux文件目錄快速自動(dòng)跳轉(zhuǎn)命令 1000次閱讀
- 簡(jiǎn)化SBC的嵌入式Linux軟件開發(fā) 1170次閱讀
- Verilog程序編寫規(guī)范 4005次閱讀
- 如何編寫第一個(gè)hello world程序 8442次閱讀
- 如何編寫漂亮的PLC程序 2325次閱讀
- 單片機(jī)的程序編寫 4336次閱讀
- Linux最常用20條命令匯總 3729次閱讀
- 關(guān)于Linux操作系統(tǒng)網(wǎng)絡(luò)驅(qū)動(dòng)程序編寫知識(shí)詳解 2481次閱讀
- Linux常見調(diào)用shell腳本的三種方法 8458次閱讀
- 教你如何編寫優(yōu)質(zhì)的嵌入式C程序? 8218次閱讀
- 如何用嵌入式Linux系統(tǒng)編寫鍵盤驅(qū)動(dòng) 1363次閱讀
- 如何高效的編寫Verilog HDL? 6247次閱讀
- 學(xué)習(xí)使用 8 種 Linux 原生命令或第三方實(shí)用程序來生成隨機(jī)密碼 4850次閱讀
下載排行
本周
- 1TC358743XBG評(píng)估板參考手冊(cè)
- 1.36 MB | 330次下載 | 免費(fèi)
- 2開關(guān)電源基礎(chǔ)知識(shí)
- 5.73 MB | 6次下載 | 免費(fèi)
- 3100W短波放大電路圖
- 0.05 MB | 4次下載 | 3 積分
- 4嵌入式linux-聊天程序設(shè)計(jì)
- 0.60 MB | 3次下載 | 免費(fèi)
- 5基于FPGA的光纖通信系統(tǒng)的設(shè)計(jì)與實(shí)現(xiàn)
- 0.61 MB | 2次下載 | 免費(fèi)
- 6基于FPGA的C8051F單片機(jī)開發(fā)板設(shè)計(jì)
- 0.70 MB | 2次下載 | 免費(fèi)
- 751單片機(jī)窗簾控制器仿真程序
- 1.93 MB | 2次下載 | 免費(fèi)
- 8基于51單片機(jī)的RGB調(diào)色燈程序仿真
- 0.86 MB | 2次下載 | 免費(fèi)
本月
- 1OrCAD10.5下載OrCAD10.5中文版軟件
- 0.00 MB | 234315次下載 | 免費(fèi)
- 2555集成電路應(yīng)用800例(新編版)
- 0.00 MB | 33564次下載 | 免費(fèi)
- 3接口電路圖大全
- 未知 | 30323次下載 | 免費(fèi)
- 4開關(guān)電源設(shè)計(jì)實(shí)例指南
- 未知 | 21548次下載 | 免費(fèi)
- 5電氣工程師手冊(cè)免費(fèi)下載(新編第二版pdf電子書)
- 0.00 MB | 15349次下載 | 免費(fèi)
- 6數(shù)字電路基礎(chǔ)pdf(下載)
- 未知 | 13750次下載 | 免費(fèi)
- 7電子制作實(shí)例集錦 下載
- 未知 | 8113次下載 | 免費(fèi)
- 8《LED驅(qū)動(dòng)電路設(shè)計(jì)》 溫德爾著
- 0.00 MB | 6653次下載 | 免費(fèi)
總榜
- 1matlab軟件下載入口
- 未知 | 935054次下載 | 免費(fèi)
- 2protel99se軟件下載(可英文版轉(zhuǎn)中文版)
- 78.1 MB | 537796次下載 | 免費(fèi)
- 3MATLAB 7.1 下載 (含軟件介紹)
- 未知 | 420026次下載 | 免費(fèi)
- 4OrCAD10.5下載OrCAD10.5中文版軟件
- 0.00 MB | 234315次下載 | 免費(fèi)
- 5Altium DXP2002下載入口
- 未知 | 233046次下載 | 免費(fèi)
- 6電路仿真軟件multisim 10.0免費(fèi)下載
- 340992 | 191185次下載 | 免費(fèi)
- 7十天學(xué)會(huì)AVR單片機(jī)與C語(yǔ)言視頻教程 下載
- 158M | 183278次下載 | 免費(fèi)
- 8proe5.0野火版下載(中文版免費(fèi)下載)
- 未知 | 138040次下載 | 免費(fèi)
評(píng)論
查看更多