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