Linux 外殼的演變之旅
對于大多數(shù)的日常計算任務(wù)來說,鼠標(biāo)的點擊操作就可以滿足要求了,但要真正利用到Linux相比于其他環(huán)境的 優(yōu)勢的話,則最終還是需要弄懂系統(tǒng)的外殼程序來輸入命令行才行??捎玫拿钔鈿こ绦蛴泻芏?,從Bash和Korn到C shell外殼,以及各種各樣有著異域風(fēng)情的和奇怪的外殼程序等不一而足。我們來了解一下哪一種外殼程序是適用于你的。
外殼或外殼程序(shell)就像是編輯器:每個人都有自己的喜好,并且會為自己的選擇進(jìn)行強烈的辯護(并會告訴你為什么你應(yīng)該換用)。誠然,一些外殼程序可以提供不同的功能,但它們都實現(xiàn)了幾十年前就已經(jīng)形成的核心理念。
關(guān) 于現(xiàn)代的外殼程序,我的***使用體驗發(fā)生在1980年代,當(dāng)時我正在SunOS上開發(fā)軟件。一旦了解了把一個程序的輸出用作另一個程序的輸入(甚至可在命 令鏈中多次這樣做)這種能力之后,我就擁有了一種簡單有效的創(chuàng)建過濾器和轉(zhuǎn)換的方式。這一核心思想提供了一種構(gòu)建簡單工具的方式,這些工具足夠靈活,能夠 以一種有益的組合來和其他工具一起使用。通過這種方式,外殼程序不僅提供了一種與內(nèi)核和設(shè)備交換的方式,而且整合了多種服務(wù)(比如說管道和過濾器),這類 服務(wù)現(xiàn)在在軟件開發(fā)中已是常見的設(shè)計模式了。
我們先從現(xiàn)代外殼程序的簡單歷史開始,然后探討Linux目前提供的一些有用的以及一些奇異的外殼程序。
外殼程序的歷史
外殼程序——或稱作命令行解釋器——有著一個很長的歷史,但這里的討論從***個 UNIX®外殼程序開始。(貝爾實驗室的)Ken Thompson在1971年開發(fā)了名為V6 shell的 ***UNIX外殼程序。與其在Multics上的前身相類似,這個外殼程序(/bin/sh)是一個獨立的用戶程序,在內(nèi)核的外部執(zhí)行。諸如通配符(參數(shù) 擴展的模式匹配,比如說*.txt)一類的概念被放在一個名為glob的單獨的實用程序中實現(xiàn),就像是if命令計算條件表達(dá)式一樣。這種分割維持了外殼程 序的短小精悍,只有不到900行的C源代碼(參閱參考資料獲得到初始源代碼的鏈接)。
外殼程序為重定向(<>和>>)和管道(|或^)引入了一種緊湊的語法,這些語法仍然在現(xiàn)代的外殼程序中使用。你也依然能夠找到對調(diào)用順序命令(使用;分隔)和異步命令(使用&分隔)的支持。
Thompson的外殼程序所缺少的是編寫腳本的功能,它的唯一目就是作為一種交換式外殼(命令解釋器)來調(diào)用命令然后查看結(jié)果。
自1977年以來的UNIX外殼程序
在Thompson外殼之后,我們從1977年的現(xiàn)代外殼程序來開始這一了解過程,Bourne外殼在這一年被引入。Bourne 外殼是AT&T貝爾實驗室的Stephen Bourne為V7 UNIX創(chuàng)建的,至今還保留了一個可用的外殼程序(在某些情況下,作為默認(rèn)的根用戶執(zhí)行外殼(root shell))。該作者是在進(jìn)行了ALGOL68編譯器方面的工作之后才開發(fā)Bourne外殼的,所以你會發(fā)現(xiàn)其語法比其他外殼程序更類似于算法語言 (Algorithmic Language ,ALGOL),而源代碼本身,盡管是用C來開發(fā)的,甚至使用了宏來賦予它一種ALGOL68的味道。
Bourne外殼有兩個主要的目的:作為一個命令解釋器,以交互方式執(zhí)行操作 系統(tǒng)的命令;以及用來編寫腳本(編寫可通過外殼調(diào)用的可重用腳本)。除了取代Thompson外殼的功能之外,Bourne外殼還提供了一些超越其前任的 優(yōu)勢。Bourne引入了控制流、循環(huán)和變量,提供了一種更函數(shù)化的語言來與操作系統(tǒng)交互(對 話式的或是非對話式的都可以)。該外殼程序還允許你把外殼腳本當(dāng)成過濾器使用,為處理信號提供集成的支持,不過其缺乏定義函數(shù)的功能。***一點是,該外殼 程序納入了一些我們今天還在使用的功能,其中包括了命令替換(使用反引號),以及在腳本內(nèi)部嵌入保留的串字面量的HERE文檔。
Bourne外殼不僅是前進(jìn)道路上的很重要的一步,而且是多種派生出來的外殼 程序的基石,這些派生外殼中的許多今天仍然用在一些典型的Linux系統(tǒng)上。圖1說明了一些重要的外殼程序的傳承關(guān)系,Bourne外殼帶來了Korn外 殼(ksh)、Almquist外殼(ash)流行的Bourne Again Shell(或稱Bash)的發(fā)展;而當(dāng)Bourne外殼發(fā)布時,C shell外殼程序(csh)已在開發(fā)之中。圖1說明了主要的傳承關(guān)系,但并未包含了所有的影響,一些跨多個外殼的顯著貢獻(xiàn)在這里并未標(biāo)注出來。
圖1. 自1977年以來的Linux外殼
我們稍后會探討其中的一些外殼程序,并例舉出一些對它們的發(fā)展有貢獻(xiàn)作用的語言和功能。
基本的外殼程序架構(gòu)
設(shè)想中的外殼程序的基礎(chǔ)架構(gòu)很簡單(已由Bourne外殼證明),正如你在圖2中見到的那樣,基本的架構(gòu)看起來類似一個管道,其中的輸入是分析和 解析,接著是符號的擴充(使用各種各樣的方法,比如說括號、波浪線、變量和參數(shù)的擴展和替換,以及文件名生成等),以及***的命令執(zhí)行(使用外殼內(nèi)置的命 令或是外部命令)。
圖2. 假想外殼程序的簡單架構(gòu)
你可在參考資料一節(jié)找到找到有關(guān)鏈接,了解開源的Bash外殼的架構(gòu)。
探討Linux的外殼程序
現(xiàn)在我們來探討一下幾個這樣的外殼程序,回顧它們所做出的貢獻(xiàn),并在每個外殼程序中檢驗一個腳本例子。要查看的外殼程序包括了C shell、Korn 外殼和Bash。
Tenex C shell外殼
1978年,當(dāng)Bill Joy還是加州大學(xué)伯克利分校的在校學(xué)生時,他為Berkeley Software Distribution (BSD) UNIX系統(tǒng)開發(fā)了C shell。五年之后,該外殼引入了Tenex系統(tǒng)(在DEC PDP系統(tǒng)上很流行)上的功能。除了命令行編輯功能之外,Tenex還引入了文件名稱和命令的補全功能。Tenex C shell(tcsh)保持了對csh的向后兼容,但提升了其整體的交互功能。tcsh是Ken Greer在卡內(nèi)基 - 梅隆大學(xué)開發(fā)出來的。
C shell的一個主要設(shè)計目標(biāo)是創(chuàng)建一種看上去類似于C語言的腳本語言,鑒于C當(dāng)時是在用的主要語言(加之操作系統(tǒng)絕大部分都是使用C來開發(fā)的),所以這是一個很實用的目標(biāo)。
Bill Joy帶到C shell中的一個實用功能是命令的歷史記錄,這一功能維持之前執(zhí)行過的命令的一個歷史,并允許用戶查看并輕松地選擇前面的命令來執(zhí)行。例如,輸入命令 history就會顯示出之前執(zhí)行過的命令,使用上下箭頭按鍵來選擇命令,或是使用!!來執(zhí)行前面的一個命令。引用前一個命令的所有參數(shù)也是可以的,比如 說,!*引用前一個命令的所有參數(shù),而!$則是引用前一個命令的***一個參數(shù)。
看一下一個簡短的tcsh腳本例子(清單1),該腳本用到了一個參數(shù)(目錄名稱),給出該目錄下的所有可執(zhí)行文件和找到的文件的數(shù)目。我在每個例子中都重用了這一腳本,以此來說明一些不同之處。
該tcsh腳本被分成了三個基本的部分,首先,需要注意的是,我是使用了shebang或稱作hashbang的符號(#!)來聲明這一文件是可 被外殼執(zhí)行程序(在本例中是tcsh二進(jìn)制執(zhí)行文件)解釋的,這就可以讓我把該文件當(dāng)成一個普通的可執(zhí)行文件來執(zhí)行,而不需要在它之前加上解釋器的二進(jìn)制 文件名。腳本維持了一個找到的可執(zhí)行文件的計數(shù),所以我把這一計數(shù)初始化為零。
清單1. 用tcsh編寫的查找所有可執(zhí)行文件的腳本
- #!/bin/tcsh
- # find all executables
- set count=0
- # Test arguments
- if ($#argv != 1) then
- echo "Usage is $0
- "
- exit 1
- endif
- # Ensure argument is a directory
- if (! -d $1) then
- echo "$1 is not a directory."
- exit 1
- endif
- # Iterate the directory, emit executable files
- foreach filename ($1/*)
- if (-x $filename) then
- echo $filename
- @ count = $count + 1
- endif
- end
- echo
- echo "$count executable files found."
- exit 0
***部分內(nèi)容測試用戶傳遞進(jìn)來的參數(shù),變量#argv代表了傳遞進(jìn)來的參數(shù)個數(shù)(不包括命令名稱自身)。你可以通過指定它們的索引來訪問這些 參數(shù)。例如,#1指向***個參數(shù)(這是argv[1]的簡寫)。該腳本預(yù)期有一個參數(shù),如果沒有找到該參數(shù)的話,就發(fā)出一條錯誤消息,使用$0來表示在控 制臺中輸入的命令(argv[0])。
第二部分內(nèi)容確保傳遞進(jìn)來的參數(shù)是一個目錄, 如果參數(shù)是一個目錄的話,運算符-d返回True。不過要注意的一點是,我先指定了一個!符號,其代表的意思是否定。通過這種方式,表達(dá)式要說的是,如果參數(shù)不是一個目錄,則發(fā)出一條錯誤消息。
***一部分內(nèi)容遍歷了目錄中的文件,測試它們是否是可執(zhí)行的。我使用了便捷的foreach這一遍歷器,其遍歷括號(本例中是一個目錄)中的每個 條目,然后在循環(huán)體中對每個條目進(jìn)行檢查,該步驟使用了運算符-x來檢查文件是否是可執(zhí)行的,如果是的話,輸出該文件名稱并且計數(shù)加一。在腳本的末尾,我 輸出可執(zhí)行文件的數(shù)目。
Korn外殼
Korn外殼(Korn shell,ksh)由David Korn設(shè)計,其差不多是和Tenex C shell同一時期引入的。Korn外殼最吸引人的功能之一是被當(dāng)成腳本語言使用,與此同時還向后兼容最初的Bourne外殼。
Korn外殼原來是專有軟件,直到2000年的時候,它才(遵照通用公共許可協(xié)議)作為開源軟件發(fā)布。除了提供很強的向后兼容Bourne外殼的 功能之外,Korn外殼還包含了一些來自其他外殼的功能(比如說csh的歷史記錄功能)。該外殼還提供了一些更先進(jìn)的功能,這些功能可以在諸如Ruby和 Python一類的現(xiàn)代腳本語言中找到——比如說,關(guān)聯(lián)數(shù)組和浮點運算。Korn外殼在許多操作系統(tǒng)上都是可用的,這些系統(tǒng)中就包括了IBM® AIX® and HP-UX;并且盡力去支持 Portable Operating System Interface for UNIX(POSIX)外殼語言的標(biāo)準(zhǔn)。
Korn外殼是從Bourne外殼派生而來的,因此其看上去更類似于Bourne外殼和Bash而不是C shell。我們來看一個Korn外殼的查找可執(zhí)行文件的例子(清單2)。
清單2. 用ksh編寫的查找所有可執(zhí)行文件的腳本
- #!/usr/bin/ksh
- # find all executables
- count=0
- # Test arguments
- if [ $# -ne 1 ] ; then
- echo "Usage is $0
- "
- exit 1
- fi
- # Ensure argument is a directory
- if [ ! -d "$1" ] ; then
- echo "$1 is not a directory."
- exit 1
- fi
- # Iterate the directory, emit executable files
- for filename in "$1"/*
- do
- if [ -x "$filename" ] ; then
- echo $filename
- count=$((count+1))
- fi
- done
- echo
- echo "$count executable files found."
- exit 0
在清單2中你首先會注意到的一件事情是,其和清單1相類似。就結(jié)構(gòu)上來說,腳本幾乎就是相同的,主要的不同體現(xiàn)在條件語句、表達(dá)式和遍歷的執(zhí)行方式上。ksh并未采用類C的測試運算符,其采用了典型的Bourne式的運算符(-eq、-ne、-lt等)。
Korn外殼在遍歷方面也有些不同,在korn外殼中,所用的是for in結(jié)構(gòu),其使用了命令替換來表示文件列表,該文件列表通過命令ls '$1/*的標(biāo)準(zhǔn)輸出來創(chuàng)建,而該命令則代表了指定名字的子目錄中的內(nèi)容。
除了前面明確了的其他功能之外,Korn還支持別名功能(使用用戶定義的串來替代一個詞)。Korn有許多其他功能在默認(rèn)情況下是禁用的(比如說文件名稱的補全),不過這些功能可由用戶來啟用。
Bourne-Again Shell外殼
Bourne-Again Shell,或稱作Bash,是一個開源的GNU項目,其目標(biāo)是取代Bourne外殼,Bash是由Brian Fox開發(fā)出來的,其已成為最常提供的外殼之一(在Linux、Darwin、Windows®、Cygwin、Novell、Haiku等等之上都有它 的身影)。顧名思義,Bash是Bourne外殼的一個超集,大多數(shù)的Bourne腳本都可不做修改就能執(zhí)行。
【編輯推薦】