[轉自LINUXEDEN]shell入門絕佳!(二)

newdayhope發表於2006-04-28
第三篇:命令的排列/命令的任務排程/命令的替換

一、命令的排列

現在您將看到一些常用的命令排列。您可能想在一行中給出所有命令,然後就可以把注意力轉移到其他地方。沒問題,shell 允許您在不同的命令之間,放上特殊的排列字元(queuing characters) 。這兒將介紹最常用的兩種。

請注意,為了看起來更清楚,我在這些字元兩旁加了空格。而在實際應用中,您不一定要這麼做,'ls -a ; du -hs'和'ls -a;du -hs'的效果是一樣的。

command1 ; command2
先執行 command1 ,不管 command1 是否出錯,接下來執行 command2 。

例如:
ls -a ; du -hs

將先在螢幕上列出目錄中的所有內容,然後列出所有目錄及其子目錄所佔磁碟大小。
command1 && command2

只有當 command1 正確執行完畢後,才執行 command2 。


例如:
ls -a bogusdir && du -hs
將返回 ls: bogusdir: No such file or directory ,而'du'則根本沒有執行(這是因為您沒有'bogusdir'目錄)。如果您將符號換成了';','du'將被執行。

為了進一步說明';'和'&&'的區別,及一般命令排列的用處,下面舉一個經典的例子:Linux 核心的編譯和安裝。

要編譯、安裝 Linux ,您需要執行一串命令:'make dep'、'make clean'、'make bzImage'、'make modules'、'make modules_install'和'make install'。如果要等一個命令完成後,再輸入下一個,再等,再輸入,……,那就太麻煩了。另一方面,每個命令只有當前面的命令都正確執行完畢後,才能開始執行。如果您用';'來排列命令,則即使有命令執行失敗,後面的也照常執行,最後,您可能在'/boot'目錄下得到一個有問題的核心映像(image)。而用'&&':

make dep && make clean && make bzImage && make modules && make modules_install && make install


不需要中途打斷,就可以編譯核心及其模組,並完成後面的安裝。
二、命令的任務排程

當您在終端裡執行一個命令或開啟一個程式時,終端要等到命令或程式執行完畢後,才能再被使用。在 Unix 中,我們稱這樣的命令或程式在前臺(foreground)執行。如果您想在終端下執行另一個命令,則需要再開啟一個新的終端。

但這裡還有一個更優雅的辦法,稱為任務排程(jobbing)或後臺(backgrounding)。當您運用任務的排程或將命令置於後臺,終端就立即解放了,這樣一來,終端立即就可以接受新的輸入。為實現這樣的目的,您只需在命令後面新增一個 & :

gqview &


告訴 shell 將圖片檢視器'GQview'放到後臺去執行(即當成 job 來執行)。


命令 jobs 將告訴您,在這個終端視窗中,執行著哪些命令與程式:

jobs

[1]+ Running gqview &

當您要關閉終端視窗時,這一點就很重要,因為關閉終端將導致所有在其中執行的任務都將被中止,在此例中,如果您關閉了終端,由這個終端開啟的 GQview 程式也將被關閉。


但如何將前臺執行的一個程式放到後臺去?沒問題:

gqview


[2]+ Stopped gqview

bg

[2]+ gqview &
組合鍵 將掛起終端中正在執行的程式,然後您就可以用 bg 命令將其放到後臺去執行。


請注意,在後臺執行圖形應用程式有時候是有用處的,這樣可以在終端下顯示這個程式的出錯資訊,雖然這對您可能沒有直接的幫助,當如果碰到了麻煩,向別人詢問時,這些出錯提示就有用武之地了。


一些圖形程式,很可能還處在測試期(Beta),儘管在後臺執行,也會在終端中輸出一些資訊。如果您對此不滿,可以用下面命令:

command &>/dev/null &


這不僅將程式送到後臺執行,還將其輸出發到'/dev/null'檔案。'/dev/null'是系統的"碎紙機" (shredder),所有送到那裡的資訊都將消失殆盡。


三、命令的替換


命令替換(Command substitution)是一項很實用的功能。我們假設,您想看看 XFree86 文件中的 'README.mouse'檔案,但您不知道這個檔案的位置。但您是位機靈的使用者,已經聽說了'locate'命令,也安裝了'slocate'包,您就可以用:

locate README.mouse

發現那個檔案在'/usr/X11R6/lib/X11/doc'。現在您就可以在終端裡用'less'或在檔案管理器中進入那個目錄然後讀取檔案。而命令替換可以給您帶來一些便捷:


less $(locate README.mouse)

一步到位。命令'locate README.mouse'的輸出(= /usr/X11R6/lib/X11/doc/README.mouse)作為'less'的引數,然後就可以顯示檔案內容了。


這種機制的語法是:

command1 $(command2)


除了'$( )',您還可以用後引號(backquote):

command1 `command2`


這樣雖然可以減少輸入,但可讀性差,而且很容易就和沒有替換功能的一般單引號混淆。我更欣賞前一種方法,但這最終起決於您。

這裡有另外一個例子。我們假設,您打算結束一個名為'rob'的程式。您先得用命令'pidof'找出相應的程式號(Process ID),然後以這個 PID 為引數,執行'kill'命令,這樣就可以結束'rob'程式。除了用:

pidof rob

567

kill 567


您還可以試試:

kill `pidof rob`

怎麼樣,效率有所提高吧?
在下一篇中,我將接著介紹 shell 的另外兩種實用的機制:檔名匹配、輸出重定向。


第四篇:檔名匹配/輸出重定向


一、檔名匹配

檔名匹配使得您不必一一寫出名稱,就可以指定多個檔案。您將用到一些特殊的字元,稱為萬用字元(wildcards)。


假設您想用'rm'命令刪除目錄下所有以字串'.bak'結尾的檔案。除了在'rm'後跟上所有檔名作為引數,您還可以用萬用字元'*':

rm *.bak


'*'可匹配一個或多個字元。在本例中,您告訴 shell 將命令'rm'的引數擴充套件到"所有以'*.bak'結尾的檔案",shell 就將擴充套件後的引數告訴'rm'命令。


您將看到,shell 在命令執行前,就將讀取並解釋命令列。正是因為這個,您才可以將萬用字元用於 shell 命令的引數中。

讓我們更進一步地來認識萬用字元'*'。假定您有個目錄,其中含檔案'124.bak'、'346.bak'及'583.bak'。您想只保留檔案'583.bak',可以用:

rm *4*.bak

shell 就將'*4*.bak'擴充套件成"所有含'4'並以'.bak'結尾的字串"。


注意到 rm 4*.bak 無法工作,因為這匹配的是以'4'開頭的檔案。由於目錄中沒有這樣的檔案,shell 將這個模式擴充套件為空的字串,故'rm'將返回出錯資訊:

rm: cannot remove `4*.bak': No such file or directory


如果您想保留檔案'345.bak',而刪除'124.bak'和'583.bak'。這看起來有些難度,因為被刪檔案的名稱除了字尾其他都不同。但幸運的是,您可以用不含有來指定檔案:

rm *[!6].bak


這將被讀為:除了以'6.bak'結尾的檔案,刪除其他所有以'.bak'結尾的檔案。您必須將取反號(negation sign)與取反字元(這裡是 6)放到括號中,不然的話,shell 會將驚歎號(exclamation mark)解釋成歷史記錄替換的開始(the beginning of a history substitution)。取反號在本篇介紹的所有匹配模式中都有效。

請注意:萬用字元'*'與取反號連用,很容易產生問題。猜猜

rm *[!6]*.bak

表示什麼?這個命令將刪除所有檔案,甚至包括名稱中包含'6'的檔案。如果您將萬用字元'*'放到了取反號前面和後面,實際上取反號將失效,因為 shell 將其解釋為"所有名稱中任何位置都不含該字元的檔案"。在我們的例子裡,只有檔案'666.bak'不符合該模式。

第二個萬用字元是問號(question mark):'?'。在匹配時,一個問號只能代表一個字元。為了示範其用途,我們在上例的假設中新增兩個新檔案:'311.bak~'和'some.text'。現在,列出所有在點號後有四個字元的檔案:

ls *.????

問號萬用字元能夠有效地避免上面提到的'取反號陷阱'(negation trap):

rm *[!4]?.*

將擴充套件成"所有除了點號前倒數第二個字元為'4'的檔案",也就是隻保留檔案'346.bak'。

您可能會問,有沒有其他匹配方式?到目前為止,您只看到了在指定位置匹配唯一字元的方法。但其實您也可以這樣:

ls [13]*

將列出所有以字元'1'或'3'開頭的檔案;在我們的例子中,檔案'124.bak'、'311.bak~'和'346.bak'匹配。注意到您必須用中括號將匹配的模式括起來,否則模式只匹配以字串'13'開頭的檔案。



接下來,您將高興地看到還可以定義匹配的範圍:

ls *[3-8]?.*


將列出所有點號前倒數第二個字元落在'3'到'8'範圍的檔案。在我們的例子中,匹配的檔案是'346.bak'和'583.bak'。


二、引用 shell 的特殊字元
但是,上面的那些機制存在一個缺點:shell 總在命令執行前,試著進行擴充套件。有時候,會變得很棘手:

l 檔名包含特殊字元。假設您在那個目錄中還有一個名為'!56.bak'的檔案。下面試圖進行模式匹配:

rm !*

rm

rm: too few arguments


shell 將'!*'解釋成歷史記錄的替換(加入前一個命令的所有引數),而不是匹配方式。

l 命令本身帶特殊字元作引數。一些 Linux 下的命令列工具,比如 (e)grep、sed、awk、find 及 locate ,都使用自己的正規表示式(regular expressions)。這些表示式與模式匹配看起來驚人地相似,但在某些地方又有所不同。


但為了使這些特殊命令生效,shell 就不能先將其當作模式匹配來解釋:

find . -name [1-9]* -print

find: paths must precede expression
應該是:

find . -name '[1-9]*' -print

./346.bak

./124.bak

./583.bak

./311.bak~


您可以透過反斜線(back slash)來引用特殊字元,比如 ! 、$ 、? 或空格:

ls !*

!56.bak



或者用(單)引號:

ls '!'*

!56.bak


請注意,要看清楚引號應該放在什麼位置。命令 ls '!*' 將查詢名為'!*'的檔案,這是由於萬用字元也在引號間,所以只能依照字面來解釋。


三、輸出重定向

Unix 的理念是彙集許多小程式,每個東東都有特殊的專長。複雜的任務不是由大型軟體完成,而是運用 shell 的機制,組合許多小程式共同完成。重定向就在其中發揮著重要的作用。


1、在多個命令間重定向


這要透過管道(pipe),由管道符號|來標識。語法是:

command1 | command2 | command3 等等


這種格式您一定已經見到過了。管道經常將一個程式的輸出送到'more'或'less'來閱讀。

ls -l | less


其中,第一個命令提供目錄內容,第二個則將其以翻頁的方式顯示。更復雜的例子如:

rpm -qa | grep ^x | less


第一個命令給出所有已安裝的 RPM 包,第二個則將其過濾(filter:'grep'),只剩下以'^x'開頭的包,第三個命令則將結果以翻頁的方式顯示。


2、重定向至檔案


有時,您希望將命令的輸出結果儲存到檔案中,或以檔案內容作為命令的引數。這可以透過'>'和'
command > file



將 command 的輸出儲存到 file 中,這將覆蓋 file 中的內容:

ls > dirlist



將當前目錄的內容儲存到'dirlist'檔案。

command < file



將 file 內容作為 command 的輸入:

sort < dirlist > sdirlist



將檔案'dirlist'的內容送到命令'sort',然後再將排序後的結果送到檔案'sdirlist'。當然,您也可以一步到位:

ls | sort > sdirlist

一種特殊的方式是'command 2> file'。這將 command 執行的出錯資訊送到 file 中。這個您到時候會需要……


另一種運算子是'>>',這將輸出新增到已存在的檔案中:

echo "string" >> file

將 string 加到檔案 file 中。這是不開啟檔案而完成編輯的好辦法!


但是,''運算子都有一個重要的限制:

command < file1 > file1

將刪除 file1 的內容,而

command < file1 >> file1


卻可以很好地工作,將加工過的 file1 內容加回到檔案中。


[@more@]

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/181629/viewspace-831181/,如需轉載,請註明出處,否則將追究法律責任。

相關文章