對話 UNIX: 第 10 部分,定制您的 Shell
如果您使用某種工具的時間足夠長,那么您就會很清楚它的用途。而且,這種工具將成為您的擴展。可以考慮 Gustav Klimt 的畫筆、Louis Armstrong 的小號和 Mark Twain 的文字表達能力。如果您是一位藝術品鑒賞家,您的“交易工具可以毫不費力地引導您的意圖、精神、以及對媒介的表達。
到目前為止,我希望您已經掌握了 Unix® 的基本技能。您能夠使用各種命令行技巧。當渴望獲得相關知識的時候,您將向無所不知的專家 請教。并且您熟練地掌握了命令的組合,可以對數據進行復雜的操作。您在命令行方面游刃有余,對于 Shell 也非常嫻熟。
下面的學習過程將指導你配置一個你自己的shell環境。
功能強大的 Shell
您已經了解了許多用以自定義您的 Shell 環境的技術:
您可以選擇希望使用的 UNIX Shell。Bourne Shell 是非常可靠的;而其他的 Shell,如 Z Shell,提供了一些新奇的思想和便利的方法,而您將發現這些都是很有幫助的。
要了解您的 UNIX 系統中可用的 Shell,可以使用 cat /etc/shells 命令。要將您的 Shell 更改為所列出的任何 Shell,可以使用 chsh 命令。以下是更改為 /bin/zsh(即 Z Shell)的一個示例。(輸入粗體顯示的文本。)
$ cat /etc/shells/bin/bash/bin/csh/bin/ksh/bin/sh/bin/tcsh/bin/zsh$ chsh -s /bin/zsh
您可以創建簡短的別名 以代替冗長的命令。
環境變量,如 PATH(它用于控制搜索程序的位置)和 TZ(它用于指定您的時區),可以保存您的首選設置,并作用于您所啟動的所有的進程。
PATH 是特別有用的。例如,如果您希望或者需要運行 Perl 的本地增強版本,那么您可以將 PATH 更改為 /usr/local/bin/perl,以代替在 /usr/bin/perl 中找到的(典型的)標準版本。
Unix 應用程序也經常使用環境變量進行自定義 的工作。例如,如果您的終端(或模擬器)允許,那么您可以使用環境變量 CLICOLOR 和 LSCOLORS 為 ls(列出目錄的內容)的輸出定制顏色。
您可以通過 Shell 內置的命令歷史,保留并且重新調用這些命令行。命令歷史保存了輸入的內容,允許您再次運行一個以前的命令。許多 Shell 還允許動態地修改以前的命令以創建新的命令。例如,Bash Shell 使用脫字符號(^)字符以執行替換:$ ls -l heroes.txt-rw-r--r-- 1 strike strike 174 Mar 1 11:25 heroes.txt$ ^heroes^villainsls -l villians.txtvillians.txt
在這個示例中,命令行 ^heroes^villains 看上去有些奇怪,它將前一個命令中的單詞 villains 替換為 heroes(如果沒有提供命令歷史列表中的命令編號,那么這是缺省的行為),并且運行這個結果,即 ls -l villians.txt。有關命令行替換的語法,可以參考您的 Shell 文檔。
如果現有的 UNIX 實用工具和 Shell 的內置特性中不包含您希望經常使用的某項特性,那么您可以編寫 Shell 腳本 以便(再次)執行復雜的操作。
正如您將在以后的“對話 UNIX文章中看到的,您還可以下載并且構建大量的附加 UNIX 實用工具,通常這些工具以開放源代碼的形式提供。事實上,使用 Google 或者 Yahoo!,您可以在很短的時間內、輕松地找到并下載一個合適的解決方案,而不用自己動手創建。(這樣就可以忙中偷閑!而用多余的時間去看看天上的云彩,好好放松一下。)
當然,可以通過許多選項對您的 Shell 進行微調,如果可以保持您的首選設置,并且一次又一次地重復使用這些設置,從一個 Shell 到另一個 Shell(比如,在不同的 X 終端窗口中)、在不同的會話中(當您注銷登錄并再次登錄)、甚至跨計算機(假設您在多個平臺上使用相同的 Shell),那就太棒了。
Shell 啟動腳本 可以提供這種持久性。在 Shell 啟動和終止時,將執行一系列腳本,以便初始化和重新設置您的環境。有些啟動腳本是系統范圍的(由您的系統管理員來配置它們),而其他的一些腳本,您可以根據需要進行自定義。
啟動腳本與微軟的 ®Windows® 中的 INI 文件有所不同。顧名思義,啟動腳本是真正的 Shell 腳本,即為實現某些工作而編寫的小程序。在這種情況下,無論何時啟動或終止 Shell,都將運行這些 Shell 腳本,并對 Shell 環境產生影響。
由此開始!
通常,每種 Shell 都提供了一些 Shell 啟動腳本,并且每種 Shell 都規定了腳本運行的順序。一般情況下,至少提供了一個系統范圍的啟動文件和一個個人(每個用戶的)的啟動文件。可以將整個 Shell 啟動序列看作一種層疊的方式:運行(潛在地)多個腳本,其效果是累積的,并且您可以在后續的腳本中否定或者改變序列中以前的參數設置。
例如,您的系統管理員可能在系統范圍的 Shell 啟動文件中為整個系統設置了一個很有幫助的缺省 Shell 提示符,例如,包括您的用戶名、當前工作目錄和命令歷史編號。然而,通過在自己的啟動腳本中,根據您的喜好重新設置 Shell 提示,您可以覆蓋這個文件。否則,如果您不改變系統范圍的設置,那么它會在您的 Shell 和環境中生效。
通常,最先執行的啟動腳本是系統范圍的,如 /etc/profile,并且由您的系統管理員對它們進行管理。系統范圍啟動文件的目的不是干擾您的工作環境,而是簡化系統特定資源的使用。例如,如果您的系統管理員堅持讓您使用安全 Shell(SSH)實用工具的更新的版本,因為原先的版本存在眾所周知的安全缺陷,他或者她可能將每個用戶的初始 PATH 變量設置為 /usr/local/bin:/bin:/usr/bin,這樣一來,將優先執行在 /usr/local/bin 中找到的可執行文件。(如果在 /usr/local/bin 中沒有找到該命令,Shell 將在 /usr/bin 中繼續查找。)系統范圍的啟動文件也可用于命名打印機,顯示關于計劃停機的公告,并為新用戶提供合理的 Shell 缺省值。(這樣就不會把新手給弄糊涂了。)
在運行了系統范圍的腳本之后,Shell 將運行用戶特定的啟動腳本。這些腳本文件非常適合保存您最喜歡的別名、環境設置和其他首選項。
為 Bash 做好準備
對于不同的 Shell,啟動腳本的個數和名字也有所不同。讓我們來看看 Bash Shell(/bin/bash)的啟動順序。Bash Shell 建立于 Unix 和 Linux® 之上,并且 Bash Shell 通常是新的系統和用戶的缺省 Shell。它也是許多其他 Shell 的代表,因此在這里可以將它作為一個很好的示例。(如果您使用的是另一種 Shell,可以參考它的文檔或者手冊頁面,以獲取關于它的啟動腳本名字和處理順序的信息。)
Bash 將搜索六 個啟動腳本,但是所有這些腳本都是可選的。即使這六個腳本都存在并且可讀,但是在任何情況下,Bash 僅執行這六個腳本的一個子集。
如果 /etc/profile 存在并且可以由該用戶進行讀取,那么 Bash 將首先執行它,這是系統范圍的啟動文件。在讀取了這個文件之后,Bash 按照順序尋找 ~/.bash_profile、~/.bash_login、~/.profile 和 ~/.bashrc,其中 ~ 是表示該用戶 home 目錄的 Shell 縮寫(也可以表示為 $HOME)。如果您退出 Bash,那么 Shell 將查找 ~/.bash_logout。
究竟執行這六個文件中的哪些,這取決于新的 Shell 的“模式。Shell 可以是登錄 Shell,是或者不是交互的。(登錄 Shell 也是一種交互的 Shell;然而,您可以強制非交互的 Shell 按照登錄 Shell 的方式工作。稍后將詳細介紹這一內容。)
在 UNIX 的早期(二十多年以前),用戶通常通過一個啞終端來訪問 UNIX 計算機。您需要在登錄提示符處輸入用戶 ID 和密碼,而系統將為您的會話產生一個新的登錄 Shell。在這種環境中,登錄 Shell 和其他的 Shell 實例(比如那些運行 Shell 腳本的 Shell)通過名字相互區別:每個登錄 Shell 的進程名字都以一個連字符作為前綴,如 -bash。這個特殊的名字(歷史悠久的 UNIX 產物)可以告訴 Shell 為登錄工作運行任何特殊的配置。
交互的 Shell 更容易解釋:如果 Shell 對您的輸入(標準輸入)作出反應并顯示相應的輸出(標準輸出),那么這個 Shell 是交互的。現在,X 終端已經取代了啞終端,但是 Shell 模式的約定和范例仍然保留了下來。通常,X 終端作為 -bash 產生 Bash,強制 Bash 執行登錄啟動序列。
對于 Bash,交互的登錄 Shell 將運行 /etc/profile,如果它存在。(如果使用 bash --login 調用 Bash,那么非交互的 Shell 也運行 /etc/profile。)接下來,交互的登錄 Shell 尋找 ~/.bash_profile,如果這個腳本存在并且是可讀的,那么將執行這個腳本。否則,Shell 將繼續嘗試執行 ~/.bash_login。如果后面的那個文件不存在或者是不可讀的,那么 Bash 最后將嘗試執行 ~/.profile。Bash 僅運行一個針對個人的啟動文件,然后立即停止啟動序列。當 Bash 登錄 Shell 退出時,它將執行 ~/.bash_logout。
如果 Bash Shell 是交互的,但不是登錄 Shell,那么 Bash 將嘗試讀取 ~/.bashrc。并不執行其他的文件。如果 Bash Shell 是非交互的,那么它將擴展 BASH_ENV 環境變量的值,并執行所指定的文件。
當然,您可以在 Bash 的標準腳本中調用自己的腳本,以便提供附加的設置。特殊的 Shell 縮寫 .(或者它的同義詞 source)表示執行另一個 Shell 腳本。例如,如果您希望在交互的登錄 Shell 和交互的非登錄 Shell 之間共享 ~/.bashrc 中的設置,可以使用下面的命令:
. ~/.bashrc
在 ~/.bash_profile 中。當 Shell 碰到點 (.) 命令時,它將立刻執行指定的 Shell 腳本。
深入研究 Shell
研究啟動序列的最好的方法是創建一些簡單的 Shell 啟動文件。例如,如果您運行 ssh farfaraway ls 命令,SSH 在遠程系統上產生的名為 farfaraway 的遠程 Shell 是一個登錄 Shell 嗎?是交互的 Shell 嗎?讓我們來找出答案。
清單 1、2、3、和 4 分別地顯示了示例 /etc/profile、~/.bash_ profile、~/.bashrc、和 ~/.bash_logout 文件。(如果這些文件已經存在,在您繼續這個練習之前請進行備份。要更改 /etc/profile,您需要在您的計算機上有超級用戶的權限。)使用您最喜愛的文本編輯器創建如下所示的文件。
清單 1 顯示了示例 /etc/profile 腳本。這個文件是第一個運行的啟動文件(如果它存在并且是可讀的)。
清單 1. 示例 /etc/profile 文件
echo "Executing /etc/profile."PATH="/bin:/sbin:/usr/bin:/usr/sbin"export PATH
清單 1 回應一條消息作為腳本的開始,并且設置最小限度的 PATH 變量。同樣地,如果 Shell 是交互的登錄 Shell,那么將運行這個文件。例如,啟動一個新的 X 終端。您應該看到與下面所示類似的內容:
Last login: Tue Apr 17 21:06:23 on ttyp1Executing /etc/profile(Interactive, login shell)Executing /Users/strike/.bash_profile(Interactive, login shell)Including /Users/strike/.aliasesstrike @ blackcat 1 $
很好!當您在 X 終端中啟動一個新的登錄 Shell 時,將看到這個可預知的序列。請注意 Shell 提示符:它反映出了用戶名、簡寫的主機名(第一個點前面的部分)、以及命令編號。
如果您在提示符處輸入 logout 或者 exit,您應該看到下面的信息:
strike @ blackcat 31 $ logoutExecuting /Users/strike/.bash_logout(Interactive, login shell)
如前所述,交互的登錄 Shell 將運行 ~/.bash_logout。
清單 2 顯示了一個示例 ~/.bash_profile 文件。這個文件是用于在啟動時自定義您的 Shell 的一種可選的方法。
清單 2. 示例 ~/.bash_profile 文件
echo "Executing $HOME/.bash_profile"echo '(Interactive, login shell)'PS1='u @ h # $ 'export PS1PAGER=/usr/bin/lessexport PAGER. .aliases
接下來,讓我們看看當您從提示符處啟動一個新的 Shell 時發生了什么。這個新的 Shell 是交互的,但它不是登錄 Shell。根據規則,~/.bashrc 是唯一等待運行的文件。
strike @ blackcat 1 $ bashExecuting /Users/strike/.bashrc(Interactive shell)blackcat:~ strike$
并且事實上,~/.bashrc 是唯一需要執行的文件。其證據就位于提示符中,底部的提示符是缺省的 Bash 提示符,而不是在 ~/.bash_profile 中定義的提示符。
要測試注銷腳本,可以輸入 exit(您不能在非登錄 Shell 中輸入 logout)。您應該看到:
blackcat:~ strike$ exitexitExecuting $HOME/.bash_logout(Interactive, login shell)strike @ blackcat 2 $
當一個交互的登錄 Shell 終止的時候,它將執行 ~/.bash_logout。您可以使用這一特性以刪除臨時文件、復制文件作為一種簡單備份方法、或者甚至啟動 rsync 以便傳播當前會話中所做的任何更改。
清單 3 顯示了一個示例 ~/.bashrc 文件。這個文件是用于非交互的 Bash Shell 實例的初始化文件。
清單 3. 示例 ~/.bashrc 文件
echo "Executing $HOME/.bashrc"echo "(Interactive shell)"PATH="/usr/local/bin:$PATH"export PATH
下面是另一個實驗:當您運行 SSH 時,會得到哪一種 Shell 呢?讓我們試試兩種變體。(您可以簡單地使用 SSH 以返回到您的本地計算機,這就好像您從遠程計算機上運行 SSH 一樣。)首先,使用 SSH 登錄到遠程計算機:
strike @ blackcat 1 $ ssh blackcatLast login: Tue Apr 17 21:17:35 2007Executing /etc/profile(Interactive, login shell)Executing /Users/strike/.bash_profile(Interactive, login shell)Including /Users/strike/.aliasesstrike @ blackcat 1 $
正如您所預期的,運行 SSH 以訪問遠程計算機將啟動一個新的登錄 Shell。接下來,當您在遠程計算機上運行一個命令時,又會發生什么事情呢?答案如下:
strike @ blackcat 3 $ ssh blackcat lsExecuting /Users/strike/.bashrc(Interactive shell)villians.txtheroes.txt
使用 SSH 遠程地運行一個命令將產生非登錄的交互 Shell。它為什么是交互的呢?這是因為遠程命令的標準輸入和標準輸出都綁定到了您的鍵盤和顯示器,這正是 SSH 的神奇之處。
清單 4 顯示了 ~/.bash_logout。這個文件將在 Shell 終止的時候運行。
清單 4. 示例 ~/.bash_logout 文件
echo "Executing $HOME/.bash_logout"echo "(Interactive, login shell)"
關于啟動文件的有價值的技巧
您使用 Shell 的次數越多,那么您就越能夠從在啟動文件中保存您的首選設置中獲利。下面是一些用于組織您的 Bash 設置的有價值的技巧和建議。(您可以在其他的 Shell 中應用相似的策略。)
如果您希望在每個 Shell(不管它是什么模式的)中使用的設置(例如,PATH),那么您可以把這些設置放入到 ~/.bashrc 中,并在 ~/.bash_profile 中使用 source 訪問文件。
如果您擁有多臺計算機(并且您的 home 目錄沒有在它們之間通過網絡文件系統 [NFS] 共享)上的帳號,那么可以使用 rsync 將網絡上所有計算機中的啟動文件保持同步。
如果您需要根據正在使用的主機來應用某些首選設置(也就是說,如果系統有特殊的資源,將使用不同的 PATH),那么可以將那些設置放在一個單獨的文件中,并且在 Shell 啟動的過程中使用 source 來訪問它。如果您選擇使用 rsync 來管理您的文件,那么可以在從文件分發列表中省略主機特定的文件。
當然,您還可以創建一個全局的腳本,并使用條件和環境變量 HOSTNAME 以選擇合適的設置。(Shell 將自動地設置 HOSTNAME,并且 HOSTNAME 將捕獲完全限定的主機名。)例如,下面是啟動文件中常見的代碼片段:
case $HOSTNAME in lab.area51.org) PATH=/opt/rocketscIEnce/bin:$PATHPS1='u @ h # $ 'export $PS1;; alien.area51.org)PATH=/opt/alien/sw/bin:$PATH;; saucer*)PATH=/opt/saucer/bin:$PATHPAGER=lessexport $PAGER;; *)PATH=/usr/local/bin:$PATHesac export $PATH
這里采用了一個 switch 語句,以便將 $HOSTNAME 的值與四個可能的值進行比較:lab.area51.org、alien.area51.org,匹配任何以字符串 saucer* 開頭的主機名的模式(如 saucer-mars 這樣的主機名將匹配;如 sauce.tomato.org 這樣的主機名將不能匹配),以及匹配所有其他內容的條件。在這個示例中,Bash 將星號(*)解釋為 Shell 運算符,而不是正則表達式運算符。當匹配到某種模式時,將執行與該模式相關聯的語句。與其他的 switch 語句不同的是,Bash 的case 僅運行一組語句。
最后,再來看看其他用戶的 Shell 啟動文件,從中得到一些靈感同時得到一些經驗。(某些用戶對這些文件和他們的 home 目錄進行了保護,這將使得您無法瀏覽到這些內容。)Joe 是否有一個非常好的、有用的提示符呢?咨詢他如何實現相同的事情。Jeanette 是否有很多加速鍵或者大量的環境變量集合,以便從實用工具方面補充一些特殊的特性呢?與她聊聊關于她的配置秘訣。最有效的得到思想和代碼的途徑就是咨詢那些在命令行方面具有豐富經驗的行家。
自定義您的 Shell
喜歡修改軟件和創作修改版本的人,聯合起來!您可以自定義您的 Shell,在您找到了某個設置或者一系列您所喜歡的設置后,將其保存到啟動文件中,并重復地使用它們。使用 rsync 或者類似的工具,將您的環境從一臺計算機傳播到另一臺計算機。
您的課程到此全部完成。請多花些時間練習相關的技能。
