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