對話 UNIX: 掌握強大的命令行
對話 Unix:您好,shell!
UNIX® 系統中一項最奇特和突出的特性就是其命令行功能。您只需輸入包含一定邏輯關系的少量文本,即可使用命令行來將有限的 UNIX 實用工具組合成無限的即時可用的數據轉換。
例如,要在當前工作目錄下的文件夾層次結構中查找獨特文件名的列表,您可在 shell 提示符下輸入以下命令:
find . -type f -print | sort | uniq
該命令行中組合了三種不同的實用工具:
find 對指定目錄進行深度搜索,在本例中,是對從 . 或 點(代表當前工作目錄)開始的文件系統進行搜索并生成滿足給定條件的所有項的名稱。這里,-type f 告訴 find 只查找文本文件。
sort,顧名思義,將對列表進行處理并生成按字母排序的新列表。
uniq(讀做 “unique),掃描列表,比較列表中的相鄰元素,以去除任何重復項。例如,假設您具有以下列表:
清單 1. 列表示例
GrouchoGrouchoChicoChicoGrouchoHarpoZeppoZeppo
uniq 可將列表精簡為:
清單 2. uniq 命令
GrouchoChicoGrouchoHarpoZeppo
但是,如果首先對 Marx Brothers 的初始列表進行排序(在連續運行中對多次出現的名稱進行重排),運行 uniq 會生成以下結果:
清單 3. 運行 uniq
ChicoGrouchoHarpoZeppo
要了解 find、sort 和 uniq 的更多擴展特性,請參閱您的 UNIX 系統中每種實用工具的 man 頁。
輸入數據、輸出數據和全部數據
獨立使用 find 時,總是以文件系統的內容作為輸入數據。但是 sort 和 uniq 則需要從標準輸入設備 (stdin) 請求數據輸入。多數情況下,您會使用鍵盤作為 stdin:例如,您需要輸入要排序的數據行。
默認情況下,find 在標準輸出設備(stdout,通常是您的終端窗口)上打印結果。sort 和 uniq 都將輸出打印到 stdout。
為了說明 stdin 和 stdout,您可在終端窗口中輸入以下文本(假設前面的百分號 (%) 為您的 shell 提示符):
清單 4. stdin 和 stdout
% sortmustachehornhatControl-D
sort 從 stdin 讀取您輸入的三行文本,并對其進行排序,然后將結果寫出到 stdout。圖 1 所示為從命令行運行 sort 和多數 Unix 命令行實用工具的示意圖。
圖 1. 典型 UNIX 命令行實用工具從 stdin 讀取并寫入到 stdout
某些實用工具,例如 find 并不從 stdin 讀取內容。它們是從系統資源(例如文件系統或系統內核)讀取需要處理的數據,然后將結果寫入到 stdout。要直觀查看 find 的工作方式,請參見以下的圖 2。
圖 2. 某些實用工具從系統資源讀取數據并將結果寫入到 stdout
除了使用 stdin 和 stdout 之外,UNIX 命令還將生成的錯誤消息輸出到一種特殊出口以便進行診斷,該出口通常并不是強制的。此出口稱為標準錯誤設備(通常簡稱為 stderr)。圖 3 所示為運行實用工具的簡單命令行。
圖 3. Unix 命令生成錯誤并輸出到特殊通道,即標準錯誤設備
如圖 3 中所示,多數 UNIX 命令從終端讀取輸入,將結果發送到終端,并將錯誤打印到終端上。默認情況下,除非另行指定,您的終端既是 stdin 的數據源,也是 stdout 和 stderr 的輸出目標。
數據傳輸
不過,您可更改 stdin 的源以及 stdout 和 stderr 的目標。您可強制 stdin 從文本文件、設備(比如連接到計算機的探頭)或網絡連接中進行讀取。類似地,您可將輸出結果發送到文件、設備或網絡連接。在 UNIX 中,所有資源都被視作文件,因此某種源或目標很容易作為另外的源或目標而被接受或產生。
更改進程數據的源和目標被稱為重定向。您可重定向 stdin 以從文件或其他源讀取數據,還可分別對 stdout 和 stderr 重定向以將數據寫到終端窗口之外的其他位置。在許多情況下,如前面所列出的初始 find 命令中,您還可重定向實用工具以從其他工具接收和為它們產生所需的數據。這就是管道 (|) 的用途。您可在命令中通過管道來生成進程鏈路,即將某條命令的數據發送到下一條命令,這類似于通過銅管將水從熱水器傳輸到洗手池中。
圖 4 所示為 find . -type f -print | sort | uniq 命令的示意圖。
圖 4. 通過管道進行鏈接的三個實用工具的示意圖
find 命令的 stdout 成為 uniq stdin,然后 uniq 的 stdout 又成為 sort 的 stdin。最后,sort 將結果打印到其標準輸出設備,即所連接的終端窗口上。這些命令的 stderr 未進行重定向,因此所有三個實用工具都會將錯誤消息打印到終端上。(來自三個實用工具的錯誤消息會混在一起,但保持正確的順序。)
如有需要,您可進一步擴展管道,將 uniq 的輸出重定向到另外的實用工具。這只需使用另一個管道即可對轉換進行擴展。例如,您可在命令之后附加 | less 以使用 less 對輸出結果進行分頁,或者您可添加 | wc -l 以統計獨特文件名的數目。(wc 為 Word count 的首字母縮寫,wc 可統計字符、單詞和行數。)
此外,您還可使用 > 來將整個命令序列的輸出結果保存到一個文件中(這將覆蓋現有的文件內容)。您可使用 >> 以將命令輸出結果附加 到現有文件之后(如果文件不存在,則創建新文件)。
另一個有用的重定向是 <。圖 5 所示為如何重定向 stdin 以從文件中進行讀取。命令 sort 從指定文件中讀取單詞列表并按字母順序進行排序。
圖 5. 重定向標準輸入以讀取文件內容
您常常會需要捕獲 stdout 和 stderr。例如,如果您正在運行大型的數據挖掘任務,則可能要檢查執行過程中的中間輸出以及可能出現的任何錯誤。您可使用重定向語法的一些變種來實現該功能:|&, >&, >>& 可分別對 stdout 和 stderr 實現管道、創建、附加功能。圖 6 所示為如何將 stdout 和 stderr 合并到單一的輸出流。
圖 6. 合并標準輸出和標準錯誤設備
Z shell 介紹
包括 Bourne shell (bash) 和 Korn shell (ksh) 在內的多數現代 Unix shell 都支持這里提到的重定向功能,盡管在這些 shell 中具體語法可能存在細微差別。(請查看您的 shell 文檔以了解詳細信息)。
重定向中的多數操作符在所有的 Unix shell 中已經連續使用了至少 25 年。但是,多數 shell 并沒有提供新的特性或采用新方式來應用重定向。例如,多數 shell 只能將輸入重定向到單個文件,因此您必須使用如 tee 等實用工具來輸出到多個目標。(類似于水管工人使用的 T 型管 (Tee),tee 只支持單個或兩個輸出。)這里提供一個使用 bash 作為 shell(命令行解釋器)的示例:
清單 5. bash 示例
bash$ lstellmebash$ cat tellmeecho Your current login, working Directory, and system are...whoamipwdsystemnamebash$ bash < tellme |& tee logYour current login and working directory are...strike/home/strikebash: systemname: command not foundbash$ lstellme logbash$ cat logYour current login and working directory arestrike/home/strikebash: systemname: command not found
盡管 UNIX shell 具有較高的專用性,且通常使用鍵盤進行交互,但某些 shell 如 bash 等也能從文件讀取輸入內容。(實際上,stdin 也是一種文件。)在前面的片段中,短語 bash < commands 告訴 bash 執行在文件 tellme 中找到的命令列表。短語 |&tee log 將 bash 的 stdout 和 stderrto 通過管道重定向到 tee 實用工具,后者將其 stdin 打印到 stdout 和 文件 log 中。
但是,如果您打算使用 bash 來處理多個文件,該怎么辦呢?cat file1 file2 file3 | bash 是一種可行的方法,這也許也是唯一的一種方法,因為在 bash 中并不支持如 bash < file1 < file2 < file3 的語法。
而且,bash 無法將輸出重定向到多個目標。例如,您可從 bash 命令行中輸入指令 bighairyscript > ~/log | mail -s "Important stuff" team。
但在相對較新的 shell 如 Z shell(zsh;請參閱參考資料)中,可以在同一命令行內處理多個輸入和輸出。例如,使用以下命令可將 stdout 保存到名為 log 的文件中并通過電子郵件發送給您自己:
清單 6. Z shell
zsh% bash < tellme > log | mail -s "Who you are" 'whoami'bash: line 4: systemname: command not foundzsh% <logYour current login, working Directory, and system are...strike/home/strike
(短語 “whoami運行命令 whoami 并將該命令的結果插入到短語所在位置。它類似于在運行命令行的其他部分之前先運行一條短小 shell 命令。)
現在我們對上一條命令從左向右進行分析。bash 命令創建文件 log 并將在 tellme 文件中找到的命令的 stdout 發送給您自己。由于 stderr 沒有通過 > 或管道進行定向,因此錯誤消息將被打印到 stdout。命令 <log 為另一個 Z shell 快捷方式,它與 cat 相同。(因此,命令 > file 等同于 cat > file。)
Z shell 還可處理多個輸入重定向。Z shell 命令行 cat < file1 < file2 < file3 等同于 cat file1 file2 file3。顯然,原有語法較后者更加繁瑣,總的來說,多個 stdout 重定向要更加常用的多。但是,如果您要運行的實用工具不接受多個輸入參數,則可使用 Z shell 的多個輸入重定向。
Z shell 中還具有其他新特性,包括更好的 globbing(通配符匹配)、先進的模式匹配和擴展的命令自動完成系統,從而減少您在命令行中的字符輸入。本系列中的后續兩篇文章將進一步探討 Z shell。
Shell 技巧
通過一些功能強大的命令行組合,能明顯提高您的工作效率。這些命令可以在所有的 shell 中工作,而不僅僅是 zsh。
使用 tar 為任何目錄創建包括符號鏈接在內的完整副本:
tar cf - /path/to/original | (mkdir -p /path/to/copy; cd /path/to/copy; tar xvf -)
第一個 tar 命令將目錄 /path/to/original 進行歸檔并將歸檔文件寫到 stdout,創建 (c) 選項后面使用的連字符 (-) 表示 stdout。括號中的命令為一個 subshell:subshell 中的命令不會影響當前 shell 的環境。mkdir -p 創建指定目錄,包括任何需要創建的中間目錄;cd 命令則切換到新目錄。第二個 tar 命令從 stdin 讀取歸檔文件并進行展開,展開 (x) 選項后面使用的連字號表示 stdin。
要在保存命令序列的 stdout 同時進行查看,可使用 less -O file 。-O 選項會將 stdin 復制到指定的 file 中。如下例所示:
sort /etc/aliases | less -Osorted
如果目錄中包含數千個文件,則您的 shell(包括 zsh,取決于文件數目及其名稱)可能無法使用通配符匹配來列舉出所有文件,因為命令行通常具有一定的字符數限制。因此,類似以下 shell 腳本:
foreach i (*)...end
可能會執行失敗。(當超出允許命令行長度時,您可能看到類似 Line length exceeded 的消息。)如果出現此類錯誤,可使用管道 xargs 實用工具。xargs 命令可從管道中讀取數據并為每行讀取內容運行指定命令。
例如,如果您要查找服務器上的所有引用 www.example.com 的網頁,可使用以下命令行:
% find / -name '*HTML' -print | xargs grep -l 'www.example.com' | less -Opages
xargs 接收來自 find 的文件名并重復運行 grep -l 以處理每個文件,而不論有多少個文件。(grep -l 在發現一個匹配項之后即打印文件的名稱并停止在該文件中的進一步匹配。) less 允許您對結果進行分頁并將列表保存在文件指定頁中。命令結果為包含字符串“www.example.com的文件名列表。
對話之旅正式開始
本文為您介紹了有關 Unix shell 的基礎知識。后續文章將更深入地討論命令行工具的更多內容以及使用技巧。通過靈活運用 UNIX 命令行功能,可實現從文件系統到整個本地局域網的所有信息和系統管理的透明化。
