英語(yǔ)是一種令人困惑的語(yǔ)言。例如,請(qǐng)考慮 moon 和 good 這兩個(gè)單詞。對(duì)外行人而言,這兩個(gè)單詞似乎應(yīng)該是押韻的,但是前者的讀音是 /mun/(根據(jù) International Pronunciation Alphabet),而后者的讀音是 /good/。似乎英語(yǔ)中的惟一規(guī)則就是例外。
UNIX shell 同樣令人困惑。例如,在 Bourne shell(和大多數(shù)常用的 UNIX shell)中,'$var'、"$var" 和 `$var` 看起來(lái)相似,但是它們會(huì)產(chǎn)生很不一樣的結(jié)果。(在本文中的 shell 示例中,每個(gè) CLI 前面都加上使用的 shell 的名稱和命令編號(hào)) 。
bash-1) # Demonstrate the differences between single-, double-, and back quotes
bash-2) var=ls
bash-3) echo '$var'
$var
bash-4) echo "$var"
ls
bash-5) echo `$var`
Rakefile app bin components config db
doc lib log patches public script src
test tmp vendor
在上面的命令序列中,把變量 var 設(shè)置為兩字母的字符串 ls。在第一個(gè) echo 命令中,單引號(hào)禁止解釋此變量,因此會(huì)按原樣顯示引號(hào)中的文本,即四字母的字符串 $var。在第 4 行代碼中,雙引號(hào)會(huì)解釋此變量,所以結(jié)果是字符串 ls。最后,反撇號(hào)解釋變量并作為子 shell 運(yùn)行中間結(jié)果。因此,`$var` 產(chǎn)生中間字符串 ls,它作為 shell 命令運(yùn)行,生成當(dāng)前目錄的內(nèi)容列表。
當(dāng)然,這三種操作符(單引號(hào)、雙引號(hào)和反撇號(hào))都有合法的用途,但是與英語(yǔ)中的例外一樣,記住和掌握這些細(xì)微差異很令人頭疼。為了進(jìn)一步證明這一點(diǎn),請(qǐng)問(wèn):$var 和 "$var" 之間有什么差異?(提示:假設(shè) $var 包含空格。)
bash-1) # Create three files and try to remove two
bash-2) touch three two one
bash-3) var="one two"
bash-4) rm "$var"
rm: one two: No such file or directory
bash-5) rm $var
bash-6) ls
three
如果一個(gè)變量包含空格,雙引號(hào)會(huì)按原樣把變量展開為一個(gè) 參數(shù)。否則,變量中的任何空格都被解釋為參數(shù)分隔符。
shell 語(yǔ)法很令人頭疼。這很糟糕,因?yàn)樗?CLI(UNIX 最強(qiáng)大的特性之一)更難掌握。上面這樣的不一致問(wèn)題甚至?xí)o UNIX 老手帶來(lái)困擾。
好在,fish (Friendly Interactive Shell) 的出現(xiàn)改變了這種混亂局面,它提供簡(jiǎn)明的語(yǔ)法,顯著改善了用戶體驗(yàn)。與其他 shell 一樣,fish 也提供重定向、快捷方式、globbing(即通配符的展開)、子 shell、制表符補(bǔ)全和變量。但是,與其他 shell 不同,fish 還提供顏色編碼的 CLI、功能豐富的命令行編輯器和大量文檔。
另外,對(duì)于執(zhí)行任何操作,fish 只提供一種方式,這非常明智。如果一個(gè) UNIX 實(shí)用程序能夠完成某一任務(wù),fish 就不會(huì)通過(guò)內(nèi)置命令重復(fù)提供此特性。例如,fish 使用系統(tǒng)范圍的應(yīng)用程序 /bin/kill 終止進(jìn)程。(與之相反,Bourne shell 通過(guò)一個(gè)內(nèi)置應(yīng)用程序?qū)崿F(xiàn)了它自己的 kill 版本??梢栽?Bourne shell 命令提示上輸入 /bin/kill 來(lái)訪問(wèn)此版本)。fish 盡可能優(yōu)先考慮簡(jiǎn)單性而不是靈活性,這顯著簡(jiǎn)化了它的使用方法。
下面,我們來(lái)安裝 fish 并體驗(yàn)它的一些特性。
獲取 fish
fish 是由 Axel Liljencrantz 創(chuàng)建的一個(gè)開放源碼項(xiàng)目,采用的許可協(xié)議是 GNU General Public License, version 2。到編寫本文時(shí),fish 的最新版本是 1.23.0,此版本于 2008 年 1 月 13 日發(fā)布。
如果使用 UNIX 或 UNIX 類系統(tǒng)(比如 Linux® 或 Mac OS X),那么應(yīng)該很容易在您的系統(tǒng)上從源代碼構(gòu)建 fish。下面是構(gòu)建步驟,見(jiàn) 清單 1:
下載程序的最新源代碼壓縮包。
解壓。
進(jìn)入源代碼目錄。
配置構(gòu)建。
運(yùn)行 make。
清單 1. 從源代碼構(gòu)建 fish
bash-1) wget http://www.fishshell.org/files/1.23.0/fish-1.23.0.tar.gz
bash-2) tar xzvf fish-1.23.0.tar.gz
bash-3) cd fish-1.23.0
bash-4) ./configure --without-xsel
checking if autoconf needs to be run... no
checking if autoheader needs to be run... no
checking for /usr/pkg/include include directory... no
...
bash-5) make
gcc -c -o function.o function.c
...
bash-6) sudo make install
...
To use fish as your login shell:
* add the line '/usr/bin/fish' to the file '/etc/shells'.
* use the command 'chsh -s /usr/bin/fish'.
如果使用 UNIX 類系統(tǒng),configure 可能不需要更多的標(biāo)志。但是,為了盡可能減少依賴性并讓 fish 采用與常用 shell 相同的目錄結(jié)構(gòu),可以分別添加 --without-xsel 和 --prefix=/usr。(如果使用 Mac OS X version 10.4 Leopard,那么還要添加參數(shù) LDFLAGS=-liconv。如果在 Mac OS X 上省略后一個(gè)選項(xiàng),那么無(wú)法編譯附帶的 fish 實(shí)用程序) 。
另外,如果使用流行的 UNIX 版本,很可能會(huì)找到預(yù)先構(gòu)建好的二進(jìn)制版本,可以把它直接安裝在您的系統(tǒng)上。例如,如果使用 Debian Linux,那么可以用 sudo apt-get install fish 命令直接安裝 fish。請(qǐng)?jiān)L問(wèn) fish 項(xiàng)目的主頁(yè),了解是否有針對(duì)您的系統(tǒng)的二進(jìn)制版本。
入門
在討論比較復(fù)雜的主題之前,我們先看看在 fish 中如何完成一些常見(jiàn)的 shell 任務(wù):
要想重定向標(biāo)準(zhǔn)輸入和標(biāo)準(zhǔn)輸出,應(yīng)該分別使用操作符 和 >。要想重定向標(biāo)準(zhǔn)錯(cuò)誤,應(yīng)該使用 ^ 操作符,見(jiàn) 圖 1。使用 ^^ 把標(biāo)準(zhǔn)錯(cuò)誤追加到一個(gè)文件中。
圖 1. 用 ^ 操作符重定向標(biāo)準(zhǔn)錯(cuò)誤
在命令 3 中,rm 產(chǎn)生的錯(cuò)誤消息被重定向到 errors 文件中。命令 4 顯示此文件的內(nèi)容。fish shell 為重定向提供各種支持,比如把描述符組合成一個(gè)流和結(jié)束描述符。
順便說(shuō)一句,文本的顏色和下劃線不是編輯出來(lái)的。shell 會(huì)在您輸入時(shí)在 CLI 中突出顯示文本。綠色表示命令名是有效的;無(wú)效的命令名用紅色表示。下劃線表示指定的文件存在。(后面一節(jié)詳細(xì)討論 shell 的反饋)。
使用圓括號(hào)(())運(yùn)行子 shell,見(jiàn) 圖 2。圓括號(hào)中的文本被解釋為一系列命令,shell 會(huì)把它們替換為執(zhí)行結(jié)果。
圖 2. 使用圓括號(hào)運(yùn)行子 shell
通過(guò)創(chuàng)建 fish 函數(shù)創(chuàng)建別名(即快捷方式)。
函數(shù)可以包含一個(gè)或多個(gè)命令,特殊變量 $argv 會(huì)自動(dòng)展開成命令行上傳遞的參數(shù)列表。
可以用 functions 命令列出已定義的所有函數(shù)。使用 functions --erase name 刪除函數(shù),例如 functions --erase ll。
還可以立即保存在命令行上編寫的任何函數(shù)。在編寫完代碼時(shí),輸入 funcsave name,例如 funcsave ll。在此之后,當(dāng)前運(yùn)行的所有 shell 和以后的所有 shell 都可以使用此函數(shù)??梢允褂?funced name 命令以交互方式編輯現(xiàn)有的函數(shù)。funced 命令提供語(yǔ)法突出顯示、制表符補(bǔ)全和自動(dòng)縮進(jìn);funcsave 和 funced 使用戶能夠更方便地定制 shell。
輸入 set variable namevalue 來(lái)設(shè)置變量。與內(nèi)置命令 functions 一樣,輸入 set --erase variable name 就可以刪除一個(gè)變量。輸入美元符號(hào)($)和變量名,就可以獲取變量中存儲(chǔ)的值,見(jiàn) 圖 3。
圖 3. 檢查一個(gè)變量是否存在
fish 提供 --query 選項(xiàng)來(lái)檢查是否定義了一個(gè)變量。如果已經(jīng)設(shè)置了此變量,set --query 返回狀態(tài)碼 0,這表示沒(méi)有出現(xiàn)錯(cuò)誤;否則,返回 1。語(yǔ)句 6 用 or 操作符連接兩個(gè)命令:第二個(gè)命令(echo)只在第一個(gè)命令失敗的情況下執(zhí)行。
那么,fish 如何處理 $var、'$var'、"$var" 和 `$var` 呢?它遵守幾條簡(jiǎn)單的規(guī)則:
如果變量包含空格,那么空格會(huì)被保留,變量總是作為單一參數(shù),見(jiàn) 圖 4。
圖 4. fish 按原樣保留字符串中嵌入的空格
如果最外邊的引號(hào)是雙引號(hào),那么展開所有變量。
如果最外邊的引號(hào)是單引號(hào),那么不展開變量。
我們來(lái)看看這些規(guī)則的實(shí)際應(yīng)用。
命令 1 創(chuàng)建四個(gè) 文件,最后一個(gè)文件的名稱包含空格。命令 3 和 4 刪除 file 變量指定的文件。命令 6 和 7 刪除 twofiles 變量指定的兩個(gè)文件。仔細(xì)看一下命令 6:因?yàn)橹禌](méi)有放在引號(hào)(單引號(hào)或雙引號(hào))中,所以不保留空格。因此,命令 7 把此變量展開成兩個(gè)參數(shù)并刪除兩個(gè)文件。命令 9 和 10 重復(fù)命令 6 和 7 中的場(chǎng)景。
命令 11 和 12 演示空格規(guī)則。盡管在命令 12 中變量沒(méi)有放在雙引號(hào)中,但是 fish 在命令 11 中保留空格。非常好。
命令 14 到 16 演示 fish 的嵌套引號(hào)規(guī)則?,F(xiàn)在,再看一下命令 11、15 和 16。shell 使用顏色編碼顯示匹配的引號(hào),以此確保語(yǔ)法正確。再看一下命令 9 和 11。后一個(gè)命令在文件名上顯示下劃線,這表示此文件存在。在命令 9 中沒(méi)有下劃線,這提示用戶某些地方出錯(cuò)了。
fish 的首字母代表 Friendly,對(duì)用戶友好是它的主要目標(biāo)。
對(duì)于新手非常有幫助的一個(gè)特性
說(shuō)到對(duì)用戶友好,就不能不提到 fish 的制表符補(bǔ)全 特性,這個(gè)新穎的特性對(duì)于 UNIX 新用戶和專家都極其有幫助。為了體驗(yàn)制表符補(bǔ)全,請(qǐng)按下面的示例操作。在每行的末尾按 Tab 鍵。
如果您不確定一個(gè)命令名的拼寫,可以在輸入幾個(gè)字母之后按 Tab,就會(huì)看到可能的完整命令的列表,見(jiàn) 圖 5。(在您的系統(tǒng)上顯示的命令列表可能與這里顯示的不一樣。此列表取決于 PATH 環(huán)境變量和您的 UNIX 系統(tǒng)的內(nèi)容) 。
圖 5. 按 Tab 補(bǔ)全命令名
注意 CLI 中的紅色文本。如果 fish 不認(rèn)識(shí)一個(gè)命令名,就用紅色顯示它。按 Tab,就會(huì)顯示以目前輸入的字母開頭的所有應(yīng)用程序名(以及簡(jiǎn)短的描述)。在空提示行上按 Tab,就會(huì)看到 PATH 中的所有應(yīng)用程序。
如果想了解一個(gè)命令的可用選項(xiàng),那么在連字符(-)或雙連字符(--)后面按 Tab,見(jiàn) 圖 6。
圖 6. 還可以通過(guò)按 Tab 補(bǔ)全一個(gè)選項(xiàng)
此時(shí),fish 會(huì)顯示可用的選項(xiàng)。shell 維護(hù)許多常用命令和選項(xiàng)的索引,您很可能能夠得到所需的幫助。但是,定制的或更復(fù)雜的實(shí)用程序可能缺少這種數(shù)據(jù)??梢蚤喿x fish 文檔,了解關(guān)于編寫自己的補(bǔ)全特性的更多信息。
還可以在輸入選項(xiàng)的幾個(gè)字母之后按 Tab,見(jiàn) 圖 7。shell 會(huì)顯示所有匹配的選項(xiàng)。
圖 7. 還可以輸入選項(xiàng)的一部分
如果您不知道一個(gè)命令處理的操作數(shù)類型,fish 在許多情況下可以提供幫助,但并不是在所有情況下都可以。例如,如果輸入 set(或 vared,即 fish 變量編輯器)和一個(gè)空格,然后按 Tab,fish 會(huì)顯示可用變量的列表。set 的操作數(shù)是一個(gè)變量。同樣,如果輸入 type 和一個(gè)空格,然后按 Tab,fish 會(huì)顯示內(nèi)置函數(shù)的列表,這些函數(shù)擴(kuò)展文件系統(tǒng)上可用的實(shí)用程序。
在一般情況下,fish 中的所有內(nèi)置函數(shù)都有上下文相關(guān)的操作數(shù)補(bǔ)全。請(qǐng)?jiān)囈幌?cd,見(jiàn) 圖 8。
圖 8. 許多命令是上下文相關(guān)的,可以顯示適當(dāng)?shù)膮?shù)
cd 函數(shù)是一個(gè) fish 函數(shù),它的操作數(shù)是一個(gè)現(xiàn)有的目錄。在輸入 cd 之后按 Tab,fish 會(huì)顯示 CDPATH 中的每個(gè)目錄包含的所有現(xiàn)有目錄。
另一個(gè)智能化補(bǔ)全與 ssh 相關(guān)。輸入 ssh 和一個(gè)空格,然后按 Tab,就會(huì)看到從 Secure Shell 已知主機(jī)文件(通常在 ~/.ssh/known_hosts 中)獲取的已知主機(jī)名列表:、
fish-1) ssh
login.example.com (Hostname)
host1.example.com (Hostname)
fish shell 還會(huì)補(bǔ)全文件名和目錄名。同樣,它會(huì)在您輸入路徑名時(shí)突出顯示正確的元素。
fish 與其他 shell 之間的一個(gè)重要差異是,它不提供歷史快捷方式,比如 !、!! 和 !$。
使用 fish 作為登錄 shell
如果您喜歡 fish,希望用它作為登錄 shell,那么把 fish 的路徑添加到正式 shell 列表(/etc/shells)中,然后運(yùn)行 chsh:
bash-1) type fish
fish is /usr/bin/fish
bash-2) sudo vi /etc/shells
Add the line /usr/bin/fish to the file if it's missing, and save the file
bash-3) cat /etc/shells
/bin/bash
/bin/csh
/bin/ksh
/bin/sh
/bin/tcsh
/bin/zsh
/usr/bin/fish
bash-4) chsh -s /usr/bin/fish
Changing shell for strike
Password: ********
bash-5) login strike
Password: ********
Last login: Wed Oct 8 15:02:21 on ttys000
Welcome to fish, the friendly interactive shell
Type help for instructions on how to use fish
fish-1) echo $SHELL
/usr/bin/fish
結(jié)束語(yǔ)
fish 中還有許多有用的特性值得研究。“fish 這條魚非常有營(yíng)養(yǎng)。”
可以調(diào)整語(yǔ)法突出顯示采用的顏色??梢酝ㄟ^(guò)編輯 ~/.config/fish/config.fish 定制啟動(dòng)過(guò)程??梢允褂猛ㄓ米兞?和 fishd 跨 shell 實(shí)例共享變量。這種 shell 還有出色的歷史搜索特性、交互式變量編輯器和交互式命令行編輯器。
最好的一點(diǎn)是,fish 本身提供大量文檔。如果需要幫助,只需在命令提示上輸入 help。
醫(yī)生的意見(jiàn)是對(duì)的:吃 “魚” 對(duì)您有益。