對話 UNIX,第 13 部分: 另外十種命令行組合
本文是“對話 Unix系列文章中的第 13 部分:我以前認(rèn)為 13 是個不吉利的數(shù)字,直到我瀏覽 Internet 搜尋這個數(shù)字之所以不吉利的原因。實際上,13 這個數(shù)字可以說是喜憂參半(請參見參考資料)。
好的方面:13 是元素鋁的原子序數(shù),而鋁可用于制作各種祭神儀式的奠酒容器;籃球職業(yè)運動員 Wilt Chamberlain 身著 13 號球衣(我們都知道,Wilt 是非常幸運的);按照某種禁忌轉(zhuǎn)換方式,13 是第 7 個質(zhì)數(shù),而數(shù)字 7 象征著幸運。
不好的方面:絞刑架有 13 級臺階;制造混亂的神“洛基和猶大,都是第 13 個到達的;并且無論您怎么對其劃分(除以 2、3、4、或者6),在餐館中 13 個人都很難坐,這可能正是洛基和猶大被認(rèn)為是局外人的原因。
陪審團最多不超過 13 個人。所以,除非您在 13 號星期五閱讀本文,并且在位于 Mockingbird Lane(這是個歷史悠久的地方)1313 號的辦公樓的第 13 層,否則都是值得慶幸的。“對話 UNIX現(xiàn)在是個長滿青春痘的青少年了。本文將介紹十種命令行組合和 Shell 訣竅,以慶祝本系列文章進入青春期。恭喜您!
臨時設(shè)置一個環(huán)境變量
環(huán)境變量,如 EDITOR 和 TZ,可能影響命令執(zhí)行的結(jié)果。(前者選擇進行文本編輯時所啟動的程序;而后者可以指定您的時區(qū)。)通常,您可以在 Shell 啟動文件中設(shè)置環(huán)境變量,以便對所有的 Shell 會話產(chǎn)生作用,并且您可以在任何時候使用像 export TZ=GMT 這樣的命令為一個 Shell 會話更改環(huán)境變量的值。
此外,您可以為單個命令臨時地修改一個環(huán)境變量的值。只需要在啟動命令行的時候設(shè)置環(huán)境變量以及您希望運行的命令即可。例如,要為單個命令更改您的首選編輯器,可以在它的前面加上 EDITOR=editor ,如下所示:
$ printenv...EDITOR=vi...$ EDITOR="pico" less bigfile
這個組合可以使用 less 對 bigfile 進行分頁。如果您在 less 中輸入 v 以編輯文件,那么將啟動 pico 而不是 vi。下面是另一個實際的使用情況:
$ dateSun Aug 5 16:14:17 EDT 2007$ TZ="Japan" dateMon Aug 6 05:14:06 JST 2007
對 TZ 進行的臨時更改將影響 date 的即時實例解釋系統(tǒng)當(dāng)前日期和時間的方式。
查看您實際正在運行的命令
大量的 Shell 特性可以影響到如何解釋您所輸入的命令名。每種 Shell 都有一個內(nèi)置命令的分類;PATH 環(huán)境變量用于指定搜索的列表和目錄;而別名可以作為簡寫。要運行一個程序可以使用許多方法,如何了解實際執(zhí)行的是什么命令呢?使用 Shell 內(nèi)置的 type 命令可以揭示實際的情況。
假設(shè)您擁有下面的這些 Shell 設(shè)置:
PATH=/bin:/usr/bin:/usr/local/binalias vi=pico
您可以在 /usr/bin 和 /usr/local/bin 中找到 Perl 的副本。要查明您正使用的是哪個 Perl,可以輸入 type perl。
$ perl -vThis is perl, v5.8.7 built for darwin-2level$ type perlperl is /use/local/bin/perl$ type -a perlperl is /usr/local/bin/perlperl is /usr/bin/perl$ type -a -w perlperl: commandperl: command
type perl 命令顯示了如何在命令行中對 perl 命令進行解釋。在這個示例中,/usr/local/bin/perl 是實際的擴展結(jié)果。type -a 命令顯示了 Shell 所知道的所有 Perl 實例,這在很大程度上依賴于 PATH 變量。
可以針對您常用的其他命令使用 type:
$ type -a vivi is an alias for picovi is /usr/bin/vi$ type -a cdcd is a shell builtincd is /usr/bin/cd
type 命令顯示出,vi 實際上是 pico 的別名。type 命令還顯示出,cd 是一個內(nèi)置的命令,并且與外部命令 /usr/bin/cd 是相同的。
使得 find 命令具有更好的可移植性
去年曾經(jīng)介紹了許多關(guān)于 find 的使用的內(nèi)容,但是我忽略了其中的一個選項,它使得 find 命令行可以移植到其他操作系統(tǒng)。
通常,Unix® 系統(tǒng)中很少使用帶空格的文件名。然而,在 Mac OS X 和 Microsoft® Windows® 中常常使用更長的、更具描述性的文件名,并且在 UNIX 中它們也變得越來越多,這是因為該操作系統(tǒng)不斷地積聚更多的桌面特性。畢竟,將一份報告保存為 2007 Business Plan 明顯要比 bizplan07.ooo 好得多。
find 命令使用嵌入的特殊字符列舉長文件名,但是,如果您希望將 find 與另一個命令組合使用,那么最安全的方法是,使用 NUL 字符(而不是空格)分隔列表中的每個文件名。讓我們來了解其中的差異。
我們假設(shè)您擁有三個文件夾,其中一個或者多個目錄的名稱中包含空格:
$ ls -1Business Plan 2007Expense ReportPictures from Spain
如果您對大量的文件運行 find 命令,并且將結(jié)果列表傳遞給 xargs,那么文件名中的空格將會導(dǎo)致錯誤:
$ find . -type f -print | xargs ls -1ls: ./Business: No such file or Directoryls: ./Expense: No such file or directoryls: ./Pictures: No such file or directoryls: 2007: No such file or directoryls: Plan: No such file or directoryls: Report: No such file or directoryls: Spain: No such file or directoryls: from: No such file or directory
傳遞給 xargs 的結(jié)果是單個字符串 . ./Business Plan 2007 ./Expense Report ./Pictures from Spain。在缺省情況下,xargs 將使用空格(或者換行符)對輸入字符串進行劃分,以便產(chǎn)生可以進行操作的一個文件列表。在這個示例中,因為文件名中包含空格,所以這樣做將會產(chǎn)生錯誤的列表,如前所述。
一種適當(dāng)?shù)摹⒖梢浦驳募夹g(shù)是使用 find -print0,加上 xargs -0,以便使用 NUL 字符對文件名進行劃分。下面是這種推薦的方法:
$ find . -type f -print0 | xargs -0 ls -1./Business Plan 2007./Expense Report./Pictures from Spain
另外,如果您希望預(yù)覽 xargs 產(chǎn)生的命令,可以添加選項 -p 或者 -t。-p 選項顯示每個合成的命令,并提示您進行確認(rèn)。輸入大寫的或者小寫的 y 以便運行命令,輸入任何其他的內(nèi)容可以拒絕該命令。-t 選項可以在執(zhí)行每個命令之前將命令回顯到 stderr。
更充分地利用 find 命令
盡管 find 非常有用,但是有兩個隱含的設(shè)置可能會限制它的結(jié)果(并使得您不知所措):-name 匹配是區(qū)分大小寫的,并且不會根據(jù)符號鏈接對文件系統(tǒng)進行遍歷。
因此,一個以 find -name '*plan*' 開頭的命令將忽略名稱中包含 Plan 字符串的文件,假設(shè)您的 home 目錄中包含名為 music 的符號鏈接,而它指向裝入到 /media/music 的 TB 級的存儲介質(zhì),那么這個命令將不會列出您的符號鏈接 music。
您可以使用 -iname 覆蓋區(qū)分大小寫的匹配,并且您可以使用 -follow 根據(jù)符號鏈接進行遍歷。下面是使用了這兩種選項的一個示例:
$ alias ls='ls -aF'$ ls -1bin/lib/src/tomb/tunes@$ find . -name '*music*' -type f -print$ find . -iname '*music*' -type f -print$ find . -name '*music*' -type f -follow -print$ find . -iname '*music*' -type f -follow -print./tunes/Muse/Origin Of Symmetry/04 Hyper Music.m4a./tunes/Radiohead/OK Computer/04 Exit Music (For A Film).MP3
正如 -F 選項生成的 @ 符號注釋所表示的,tunes 是一個符號鏈接。要查找名稱中包含字符串“music的任何變體的所有歌曲,您必須使用 -iname *music*。要遍歷到 tunes 所指向的文件系統(tǒng)層次結(jié)構(gòu),您必須使用 -follow。
為了使得 find 更具可移植性,并且類似于 Spotlight 的搜索特性,那么應(yīng)該使用 -print0 -follow -iname pattern 。
收集許多命令的輸出的簡單方法
通過使用 > output 和 >> output 修飾符,您可以很容易地捕獲一個命令行的輸出,其中前者用于創(chuàng)建或者覆蓋文件 output ,而后者則將內(nèi)容追加到 output 。您可以組合使用任何修飾符以生成一系列命令的文本,如果您正嘗試對系統(tǒng)狀態(tài)進行快照,這種方法是非常有價值的,例如:
$ ps > state.`date '+%F'`$ w >> state.`date '+%F'`
反勾號或反引號操作符 (``) 可以對命令進行擴展。在 Shell 對命令行進行解釋時,將執(zhí)行反勾號之間的命令,并在最終的擴展結(jié)果中使用該命令的輸出。在本示例中,參數(shù)周圍的單引號用于保持參數(shù)不變,從而可以避免 Shell 對 + 和 % 進行解釋。
在執(zhí)行了這兩個命令之后,創(chuàng)建了文件 state.YYYY-MM-DD,如 state.2007-08-05,其內(nèi)容與以下所示類似:
PID TTY TIME CMD9997 pts/100:00:00 zsh10351 pts/100:00:00 ps17:56:04 up 21 days, 2:53, 2 users, load average: 0.89, 0.94, 0.91USER TTY FROM LOGIN@ IDLE JCPU PCPU WHATadamgood pts/0c-67-169-182-255 Sat170.00s 0.37s 0.36s pinemstreich pts/1cpe-071-065-224- 17:170.00s 0.01s 0.00s w
不過,每次輸入反勾號操作是非常麻煩的。您可以使用下面的命令來代替這個序列:
$ file=state.`date '+%F'`$ ps > $file$ w >> $file
但是,雖然這樣做稍微有效一些,但仍然可能出現(xiàn)錯誤,因為在第二個或者后續(xù)的命令中,很可能使用 > 而不是 >>。要捕獲一系列命令的輸出,最簡單的方法是使用大括號 ({ }) 將命令括起來。
$ { ps; w } > state.`date '+%F'`
ps 命令運行(列出用戶當(dāng)前的進程),然后是 w(它將顯示誰正在使用這臺計算機),并將收集到的輸出保存到一個文件中。
注意: 您還可以在圓括號中嵌入一個命令序列,以得到相同的結(jié)果;然而,兩者之間有一個重要的區(qū)別。在圓括號中的系列命令將在一個子 Shell 中運行,并且不會對當(dāng)前 Shell 的狀態(tài)產(chǎn)生影響。
例如,您可能希望運行這個序列:$ { cd $HOME; ls -1}; pwd
它將與下面的命令產(chǎn)生相同的輸出:$ (cd $HOME; ls); pwd
大括號中的命令更改了當(dāng)前 Shell 的工作目錄。后面的這種技術(shù)則無能為力。是使用組合還是子 Shell,這取決于您的目的,盡管子 Shell 的功能更強大一些,下面將對其進行描述。
子 Shell 可以為您提供幫助!
盡管通常運行子 Shell 將聚合的輸出通過管道傳遞給單個命令,但您還可以使用子 Shell 對命令進行擴展,就像反勾號那樣。然而更有價值的是,子 Shell 可以包含另一個子 Shell,所以還可以進行嵌套擴展。
讓我們來看看下面簡單的例子。
$ {ps; w} > state.$(date '+%F')
這個命令與 { ps; w } > state.`date '+%F'` 是相同的。$( ) 符號運行圓括號中的命令,然后使用輸出來替換自己。換句話說,$() 可以進行擴展,就像反勾號一樣。然而,與反勾號不同的是,$( ) 非常復(fù)雜,并且甚至可以包括其他 $( ) 擴展。下面提供了一些示例:
$ (cd $(grep strike /etc/passwd | cut -f6 -d':'); ls)
這個命令在密碼文件中搜索用戶 strike 對應(yīng)的條目,提取其 home 目錄(密碼文件中的第 6 個字段,如果您從 0 開始數(shù))字段,更改到這個目錄,并列出其中的內(nèi)容。grep /etc/passwd strike | cut -f6 -d':' 的輸出將在執(zhí)行任何其他操作之前進行擴展。
下面是另一個示例,這次的用戶名來自于 whoami 的結(jié)果:
(cd $(grep $(whoami) /etc/passwd | cut -f6 -d':'); ls)
因為子 Shell 有許多用途,所以與組合或者反勾號操作符相比,您可能更喜歡使用它。
不再輸入長路徑名
有些特性,如 PATH 和 MANPATH 環(huán)境變量,可以減少輸入工作量。這兩個變量分別為搜索可執(zhí)行文件和 man 頁面定義了一系列目錄。
Shell 支持另一個搜索路徑:CDPATH。顧名思義,CDPATH 列出了搜索命名目錄的目錄列表。讓我們看看它是如何工作的。
假設(shè)您的 home 目錄中有三個目錄,它們分別是 tomb、current 和 personal。tomb 目錄中包含舊的工作項目;current 目錄中包含當(dāng)前工作的內(nèi)容;而 personal 目錄中包含您所感興趣的一些文件和內(nèi)容。執(zhí)行 ls -R tomb current personal 命令可以得到與下面所示類似的內(nèi)容:
$ ls -R tomb current personalcurrent:./../ einstein/ herbIE/personal:./ ../ fishing/ novel/tomb:./ ../ mariner/ marvin/ voyager/
對于這種結(jié)構(gòu),如果不使用 CDPATH,要更改到任何目錄都需要記住文件夾的位置,并輸入完全限定的(或者相對的)路徑名:
$ cd ~/tomb/mariner$ cd ~/personal/novel$ cd ~/current/einstein
為了簡化這項任務(wù),可以將 CDPATH 設(shè)置為您所需要的搜索命名目錄的目錄列表:
$ export CDPATH=.:~/:..:../..:
這是 CDPATH 的最小設(shè)置。它將按順序搜索當(dāng)前目錄(.,或者“點)、您的 home 目錄 (~/)、父目錄(..,或者“點點)、父目錄的父目錄目錄 (../..)。最小設(shè)置首先搜索本地目錄以及附近的一些目錄。
在設(shè)置了這個 CDPATH 之后,您可以快速地更改到任何頂層目錄:
$ pwd/tmp$ cd current/home/strike/current$ cd personal/fishing/home/strike/personal/fishing$ cd novel/home/strike/personal/novel$ cd /tmp$ cd personal/novel/home/strike/personal/novel$ cd /tmp$ cd novelcd: no such file or Directory: novel
除最后一個 cd 命令之外,所有命令的參數(shù)都在 CDPATH 中存在匹配的目錄。然而,因為 personal 目錄不在 CDPATH 中,所以無法找到 novel(如果您位于相對路徑之外)。
如果您希望搜索 personal 目錄和其他的兩個目錄,那么可以將它們添加到 CDPATH 的最后一個冒號的后面,或者根據(jù)您所需要的搜索順序進行添加。添加三個目錄,假設(shè)您的 Shell 啟動文件中包含前面的 export 命令:
$ export CDPATH=$CDPATH:~/current:~/tomb:~/personal
現(xiàn)在,您只需要輸入希望切換到的目錄的名稱即可:
$ cd current/home/strike/current$ cd /tmp$ cd einstein/home/strike/current/einstein$ cd fishing/home/strike/personal/fishing$ cd personal/novel/home/strike/personal/novel
與 PATH 和 MANPATH 一樣,如果 CDPATH 中的多個條目都包含匹配項,那么在找到第一個匹配項后將停止搜索。例如,如果您向 tomb 中添加一個名為 novel 的目錄,那么 cd novel 命令將得到 ~/tomb/novel。
$ mkdir ~/tomb/novel$ cd /tmp$ cd novel/home/strike/tomb/novel$ cd personal/novel/home/strike/personal/novel
如果其條目中包含唯一的目錄名,那么 CDPATH 是最有效的。否則,必須輸入足夠長的路徑以進行區(qū)別,比如 personal/novel。
取得事半功倍的效果
您已經(jīng)看到了許多示例,說明文本文件在 Unix 系統(tǒng)中有著廣泛的用途。大多數(shù)系統(tǒng)啟動文件都是文本文件,包括 Shell 腳本、配置文件,當(dāng)然還包括數(shù)據(jù)文件。除了文本編輯器之外,最有價值的實用工具就是翻頁工具 (pager)、或者允許您逐頁瀏覽文本文件的應(yīng)用程序。
應(yīng)用程序 less 是最常用的翻頁工具之一,并且它提供了大量的選項以調(diào)整它的行為。事實上,您可以將 LESS 環(huán)境變量設(shè)置為相關(guān)選項的列表,以便控制 less 的缺省工作方式。下面是一組有用的選項:
export LESS="-Nmsx4"
-N 可以顯示行號。
-m 能夠以百分比的形式顯示在文件的當(dāng)前位置。
-s 可以將多個空行“壓縮或者減少為單個空行。
-x4 可以將制表位設(shè)置為四個空格。
請仔細地閱讀 less 的 man 頁面,以便找到對您最有幫助的選項。
從下到上閱讀文件
在 UNIX 系統(tǒng)中,許多文件會不斷地增大,直到被截斷或者進行存檔。例如,最重要的一些系統(tǒng)處理,如電子郵件傳輸和遠程訪問、持續(xù)日志記錄活動,都會在文件的末尾添加新的條目。并且是最感興趣的日志文件的末尾。如果某個服務(wù)崩潰了,那么最后發(fā)生的事件將提供最有價值的線索。
有兩種方法可以逆序顯示文件中的行:tac(將 cat 反過來)和 tail -r 命令。
$ cat smallfileabc$ tac smallfilecba$ tail -r smallfilecba
您可能會發(fā)現(xiàn) tac 更加實用一些,因為它將顯示整個文件,這與 tail 是不同的,后者將對輸出進行截斷,只顯示若干行的內(nèi)容。例如,您可以組合使用 tac 和 less 以創(chuàng)建一個別名,用于對文件進行逆序分頁:
$ alias rless="LESSOPEN='|tac %s' less"$ rless smallfilecba
rless 別名臨時地將 LESSOPEN 設(shè)置為 |tac %s,這是特定于 less 的一個環(huán)境變量。這樣可以強制使用 tac 對每個文件(%s 是文件名的占位符)進行預(yù)處理(所以使用了管道 |)。
下面提供了這個相同技巧的另一種變體,但是它使用了 perl 而不是 tac,在您的系統(tǒng)中可能無法使用這個命令:
LESSOPEN="|perl -e 'print reverse (<>)' %s" less small
包含 perl 的那行命令表示“將所有的輸入行讀入一個匿名數(shù)組 ((<>)),顛倒元素的順序,并打印這個新的數(shù)組。
進行新的數(shù)學(xué)運算
如果您需要計算一個結(jié)果,那么并不需要轉(zhuǎn)到一個新的應(yīng)用程序。您可以繼續(xù)在命令行中完成這項任務(wù)。您可以使用 dc(這是一種逆波蘭式計算器),或者 bc(這是一種用于數(shù)學(xué)運算的完整的腳本編程語言)。或者,如果您需要馬上獲得答案,那么可以使用命令行和 $(( )) 操作符。
$ echo $(( 100 / 10 ))10$ echo $(( 10 ** 2 ))100
Shell 并沒有提供大量的算術(shù)操作符,但是已經(jīng)足以完成大多數(shù)編程任務(wù),包括移位、求余和比較。
還有很多的內(nèi)容需要學(xué)習(xí)
“對話 Unix已經(jīng)是第 13 部分了,但是仍然有許多內(nèi)容需要介紹。我們需要學(xué)習(xí)更多的命令和技巧、研究各種各樣的相關(guān)概念,當(dāng)然還包括大量的開放源代碼軟件,以便提高您的工作效率。
還有一點就是,必須克服各種困難。高年級學(xué)生有時會捉弄人,有時的確令人尷尬,但他們相處融洽。也許我看起來像是在倚老賣老了!. . 孩子們相處很融洽,是吧!
感謝您的閱讀!我希望您能夠喜歡本專欄。
相關(guān)文章:
1. 對話 UNIX,第 6 部分: 通過腳本實現(xiàn)操作的自動化2. 對話 UNIX: Squirrel--可移植的 shell 和腳本語言3. 對話 UNIX,第 9 部分: 正則表達式4. 對話 UNIX,第 7 部分: 命令行慣用語5. 對話 UNIX:第 2 部分: 做得多不如做得巧6. 對話 UNIX,第 4 部分: UNIX 所有權(quán)和權(quán)限管理7. 對話 UNIX,第 3 部分: 在命令行中完成所有的工作8. 對話 UNIX: 第 10 部分,定制您的 Shell9. 對話 UNIX,第 11 部分: 漫談 UNIX 文件系統(tǒng)10. 對話 UNIX: 在 Windows 上使用 Cygwin
