天天看點

linux指令技巧



技巧:

用 cat

合并檔案

您常常需要将幾個檔案處理成一個檔案并将這種處理的結果儲存到一個單獨的輸出檔案。

cat

(“concatenate”的縮寫)指令在其輸入上接受一個或多個檔案并将它們作為一個單獨的檔案列印到它的輸出。例如,

cat chapter01 chapter02 chapter03 > book

将三個chapterXX

檔案儲存在一個單獨的book

檔案中。

輸入檔案按照它們在

指令後的排列順序被列印,是以,要調換資訊的順序,就必須先調換輸入檔案的順序。此外,當需要處理的檔案數目對于您來說過大而無法手工輸入這些檔案的名稱

時,您可以使用通配符,如在

cat chapter* > book中使用的那樣,記住,檔案名将會按升序排列。當您突然發現chapter13

被發送到輸出中時會在chapter2之前,而會在

chapter02之後時,這會引起很有意思的問題。

的輸出沒有被重定向到一個檔案或另一個指令的标準輸出時,

表現出來的行為與多數指令行工具一樣,即将其輸出發送到控制台。

這意味着您可以使用

來顯示檔案;例如,您可以使用

cat /etc/passwd來顯示系統密碼檔案的内容。為友善起見,您應該用less檢視大檔案,如在

less /etc/passwd中那樣(您可以通過輸入

man less

學習更多關于less

的知識)。

盡管

主要用于合并檔案,您還可以将它用于輸入的簡單自動處理。例如,您可以使用

一個單獨的空白行來除去多行空白行(使用

-s

選項),這是一個在您将源代碼公諸于世前進行清除工作的好辦法。遺憾的是,

并沒有用于一次清除所有空白行的選項。但這并不是什麼大問題,因為您

可以使用友善的

sed

指令将這些空白行除去:

清單1.

使用sed

與cat

除去空白行

$ cat -s /etc/X11/XF86Config | sed '/^[[:space:]]*$/d'

...

# Multiple FontPath entries are allowed (they are concatenated together)

# By default, Red Hat 6.0 and later now use a font server independent of

# the X server to render fonts.

FontPath   "/usr/X11R6/lib/X11/fonts/TrueType"

FontPath   "unix/:7100"

EndSection

對于讀取配置檔案和HTML

頁面的源檔案,特别是那些由腳本生成的插入了不必要新行的源檔案,以及那些包含大型條件結構(其各個項之間已經用空行分開)的源檔案來說,空白行緊縮是一個友善的技巧。

的另外一個重要的功能是它可以對行進行編号。這種功能對于程式文檔的

編制以及法律和科學文檔的編制很友善。列印在左邊的行号使得參考文檔的某一部分變得容易。這在程式設計、科學研究、業務報告或甚至是立法工作中都是非常重要的。

對行進行編号功能有兩個選項:

-b

選項(隻能對非空白行進行編号)和

-n

選項(可以對所有行進行編号):

清單2.

對行進行編号

$ cat -b /etc/X11/XF86Config

14  # Multiple FontPath entries are allowed (they are concatenated together)

15  # By default, Red Hat 6.0 and later now use a font server independent of

16  # the X server to render fonts.

17      FontPath   "/usr/X11R6/lib/X11/fonts/TrueType"

18      FontPath   "unix/:7100"

19  EndSection

$ cat -n /etc/X11/XF86Config

20  # Multiple FontPath entries are allowed (they are concatenated together)

21  # By default, Red Hat 6.0 and later now use a font server independent of

22  # the X server to render fonts.

23 

24      FontPath   "/usr/X11R6/lib/X11/fonts/TrueType"

25      FontPath   "unix/:7100"

26 

27  EndSection

還可以在您檢視包含如制表符這樣的非列印字元的檔案時起幫助作用。

您可以用以下選項來顯示制表符:

-T

将制表符顯示為

^I

-v

顯示非列印字元,除了換行符和制表符,它們使用各自效果相當的“控制序列”。

例如,當您處理一個在 Windows

系統中生成的檔案時,這個檔案将使用 Control-M(

^M

)來标記行的結束。對于代碼大于 127

的字元,它們的前面将會被加上

M-

(表示“meta”),這與其它系統中在字元前面加上

Alt-

相當。

-E

在每一行的結束處添加美元符(

$

)。

清單3.

顯示非列印字元

$ cat -t /etc/X11/XF86Config

^IFontPath^I"/usr/X11R6/lib/X11/fonts/TrueType"

^IFontPath^I"unix/:7100"

$ cat -E /etc/X11/XF86Config

# Multiple FontPath entries are allowed (they are concatenated together)$

# By default, Red Hat 6.0 and later now use a font server independent of$

# the X server to render fonts.$

FontPath   "/usr/X11R6/lib/X11/fonts/TrueType"$

FontPath   "unix/:7100"$

EndSection$

$ cat -v /etc/X11/XF86Config

^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@M-|M-8^X^@^@^@

P^@^O"M-X^O M-@^M^@^@^@M-^@^O"M-@M-k^@M-8*^@

@M-^H$M-@M-9|A(M-@)M-yM-|M-sM-*M-hW^A^@^@j^@

M-|M-sM-%1M-@M-9^@^B^@^@M-sM-+fM-^A= ^@ ^@

F^@^@  

^@M-9^@^H^@^@M-sM-$M-G^E(l!M-@M-^?

^IM-A5^@^@^D^@PM-^]M-^\X1M-H%^@^@^D^@tyM-G

下回我們将看一下

head

和tail

。到時候見!

使用 head

和 tail

以塊方式讀取文本流

假定您想隻處理檔案的一部分,譬如頭幾行或後幾行,那您該怎麼做呢?請使用

head

(它将頭 10

行發送至标準輸出)或

tail

(它将後 10

行發送至标準輸出)。

您可以通過使用

-n

選項改變這些指令發送至其标準輸出的行數(當然,輸出結果将随XF86Config

檔案的内容而不同):

清單 1.

将 XF86Config

中標明行數的内容發送至标準輸出

$ head -n 4 /etc/X11/XF86Config      
# File generated by anaconda.      
# **********************************************************************      
# Refer to the XF86Config(4/5) man page for details about the format of      
# this file.      
$ tail -n 4 /etc/X11/XF86Config      
Modes       "1600x1200"      
ViewPort    0 0      
EndSubsection      
EndSection      

如果您想讓

head

tail

以位元組而不是以行為機關,那該怎麼辦呢?您可以用

-c

選項代替

-n

選項。是以,要顯示前 200個字元,請使用

head -c 200

file

,或者使用

tail -c 200

來顯示後 200

個字元。如果數字後面跟有

b

(表示塊(block)),那麼這個數字将被乘以512。類似地,跟有

k

(表示千位元組(kilobyte))表示用

1024 去乘給定的數字,而跟有

m

(表示兆位元組(megabyte))表示用

1048576 位元組去乘給定的數字。

請記住,

headfile1 file2 file3

cat file1 file2 file3 | head

之間有重大差别。前者将列印每個檔案指定行數的内容,不同檔案的内容之間用頭資訊隔開,頭資訊以

==>

後跟檔案名開頭。後者将列印由cat

指令後所列檔案組成的輸入流中指定行數的内容,但将把輸入流作為單個檔案對待。可以使用

-q

(表示靜默(quiet))選項關閉檔案名頭資訊。與

-q

相反的是

-v

(表示詳列(verbose))。

假如您要處理的檔案在處理期間一直在發生變化(比如,當您讓

head

tail

讀取來自正在被另一個指令寫入的檔案的資料時,就是這種情況),請使用

-f

選項讓 tail

持續讀取來自指定檔案的資料并将這些資料發送至tail

自己的标準輸出中。通過管道發送資料時該選項會被忽略。是以,

cat file | tail -f

将不會得到所期望的結果,但

tail -f file

則可以。

(如果

tail

正在讀取的檔案不止一個,那麼各行内容之間将用标準頭資訊隔開,以指明它們來自哪個檔案,标準頭資訊以

==>

開頭。)

這個選項用于監視系統日志再合适不過,譬如,在單獨的終端視窗(或單獨的控制台)中執行的

tail -f /var/log/access.log

将持續列印每次點選後新添加的Apache

通路日志條目,一直到您用Ctrl-C

停止它為止。

通過組合使用

head

tail

,可以從檔案的中間部分讀取給定長度的一塊資料!下面說明如何做到:假定您想從檔案開頭算起第1000

位元組處開始讀取一塊789

位元組的資料。可以使用

cat file | head -c 1788| tail -c 789

來解決這一問題。

tac

指令逆序排序檔案

如果您想對檔案中的各行進行逆序排序,該怎麼做呢?這就要用到

tac

指令。(請注意,

tac

cat

逆序拼寫而成。)該指令對所列出的檔案中的各行或各段進行逆序排序。

該指令不能逆序排序各檔案的順序 —

這個任務您得自己做,以逆向順序在

tac

指令後列出各檔案即可。作為說明

tac

的工作原理的示例,請在您的主目錄中對一些檔案使用

ls -l | tail

ls -l | tail | tac

,比較其結果。

下回我們将看看

sort

tsort

指令。到時候見!

用 sort

和 tsort

對檔案進行排序

盡管可以用 Perl

或 Awk

編寫進階排序應用程式,但并非總是有此必要,而且這樣的工作也常常令人感到頭疼。使用

sort

指令,您同樣可以實作您所需的大多數功能,而且更容易,它可以對多個檔案中的行進行排序、合并檔案甚至可以檢視是否有必要對它們進行排序。您可以指定排序鍵(用于比較的行中的一部分),也可不指定,後一種情況下

sort

就比較所有行。

是以,如果您想對密碼檔案進行排序,就可以使用下列指令(請注意,您不能将輸出直接發送到輸入檔案,因為這會破壞輸入檔案。這就是為何您需要将它發送到臨時檔案中,然後将該檔案重命名為/etc/passwd

的原因,如下所示)。

簡單排序

$ su -       
# sort /etc/passwd > /etc/passwd-new      
# mv /etc/passwd-new /etc/passwd      

有關 sort

的更多資訊

通過打開

有關排序操作的GNU

手冊頁

來學習手冊頁中的内容,或者通過在指令行中輸入

man sort

man tsort

在新的終端視窗的手冊頁或資訊頁中檢視這些選項。

如果您想倒轉排序的次序,則應當使用

-r

選項。您還可以用

-u

選項來禁止列印相同的行。

sort

的一個非常實用的特性是它用字段鍵進行排序的能力。字段是一個文本字元串,通過某個字元與其它字段分隔開。例如,/etc/passwd中的字段是用冒号(

:

)分隔的。是以,如果願意的話,您可以按照使用者辨別、組辨別、注釋字段、主目錄或shell

對/etc/passwd

進行排序。要做到這一點,請使用

-t

選項,其後跟着用作分隔符的字元,接着是用作排序鍵的字段編号,再跟作為鍵的最後一個字段的編号;例如,

sort -t : -k 5,5/etc/passwd

按照注釋字段對密碼檔案進行排序,該字段中存儲了完整的使用者名(如“John Smith”)。而

sort -t : -k 3,4 /etc/passwd

同時使用使用者辨別群組辨別對同一個檔案進行排序。如果您省略了第二個數字,那麼

sort

會假定鍵是從給定的字段開始,一直到每一行的末尾。動手試一試,并觀察其中的差別(當數字排序看上去有錯時,請添加

-g

選項)。

還要注意的是,空白過渡是預設的分隔符,是以,如果字段已經用空白字元分隔了,那麼您可以省略分隔符,隻使用

-t

(另注:字段的編号是從 1

開始的)。

為了更好地進行控制,您可以使用鍵和偏移量。偏移量是用點與鍵相分隔的,比如在

-k 1.3,5.7

中,表示排序鍵應當從第 1

個字段的第 3

個字元開始,到第 5

個字段的第 7

個字元結束(偏移量也是從 1開始編号的)。何時會用得着偏移量呢?嗯,我時常用它來對Apache

日志進行排序;鍵和偏移量表示法讓我跳過了日期字段。

另一個要關注的選項是

-b

,它告知

sort

忽略空白字元(空格、跳格等等)并将行中的第一個非空白字元當做是排序鍵的開始。還有,如果您使用該選項,那麼将從第一個非空白字元開始計算偏移量(當字段分隔符不是空白字元,且字段可能包含以空白字元開頭的字元串時,這非常有用)。

可以用下面這些選項來進一步修改排序算法:

-d

(隻将字母、數字和空白用作排序鍵)、

-f

(關閉大小寫區分,認為小寫和大寫字元是一樣的)、

-i

(忽略非列印的 ASCII

字元)、

-M

(使用三個字母的月份名稱縮寫:JAN、FEB、MAR

… 來對行進行排序)和

-n

(隻用數字、-

和逗号或另外一個千位分隔符對行進行排序)。這些選項以及

-b

-r

選項可以用作鍵編号的一部分,在這種情況下,它們隻适用于該鍵而非全局,其作用就跟在鍵定義外使用它時一樣。

以鍵編号的用法為例,請考慮:

sort -t: -k4g,4 -k 3gr,3 /etc/passwd

這條指令将按照組辨別對 passwd

檔案進行排序,而在組内按照使用者辨別進行逆向排序。

但是這并非

sort

的全部能力。如果您所使用的鍵不能用來确定哪一行是在先,那麼它也可以解決這類平局問題。增加一個解決平局問題的提示,請添加另一個

-k

選項,讓它跟在字段和(可選的)偏移量後面,使用與前面用于定義鍵相同的表示法;例如,

sort -k 3.4,4.5 -k7.3,9.4 /etc/passwd

對行進行排序時,使用從第 3

個鍵的第 4

個字元開始到第 4

個鍵的第 5

個字元結束的鍵,然後再采用從第7

個字段的第3

個字元到第9

個字段的第4

個字元結束的鍵來解決上述難題。

最後一組選項處理輸入、輸出和臨時檔案。例如,

-c

選項,當它用于

sort -c <

中時,它檢查輸入檔案是否已進行了排序(您也可以使用其它選項),如果已進行了排序,則報告一個錯誤。這樣,在處理可能需要花很長時間進行排序的大型檔案之前,可以很友善地對其進行檢查。當您将

-u

選項和

-c

選項一起使用時,會被解釋為一個請求:檢查輸入檔案中不存在兩個相同的行。

當您處理大型檔案時還有一個很重要的

-T

選項,它用于為臨時檔案(這些臨時檔案在

sort

完成工作之後會被除去)指定其它目錄,而不是預設的

/tmp

目錄。

您可以使用

sort

來同時處理多個檔案,這樣做的方式基本上有兩種:首先可以使用

cat

來并置它們,如下所示:

cat file1file2 file3 | sort > outfile

或者,可以使用下面這個指令:

sort -m file1file2 file3 > outfile

第二種情況有個條件:在将所有輸入檔案一起進行

sort -m

之前,每個檔案都必須經過排序。這看起來似乎是個不必要的負擔,但事實上這加快了工作速度并節約了寶貴的系統資源。對了,别忘了

-m

選項。在這裡您可以使用

-u

如果需要某種更深奧的排序方法,您可能要檢視

tsort

指令,該指令對檔案執行拓撲排序。拓撲排序和标準

sort

之間的差别如清單 2

所示(您可以從

參考資料

下載下傳happybirthday.txt)。

清單 2.

拓撲排序和标準排序之間的差别

        $ cat happybirthday.txt      
Happy Birthday to You!      
Happy Birthday to You!      
Happy Birthday Dear Tux!      
Happy Birthday to You!      
        $ sort happybirthday.txt      
Happy Birthday Dear Tux!      
Happy Birthday to You!      
Happy Birthday to You!      
Happy Birthday to You!      
        >$ tsort happybirthday.txt      
Dear      
Happy      
to      
Tux!      
Birthday      
You!      

當然,對于

tsort

的使用來說,這并非一個非常有用的示範,隻是舉例說明了這兩個指令輸出的不同。

tsort

通常用于解決一種邏輯問題,即必須通過觀察到的部分次序預測出整個次序;例如(來自tsort

資訊頁中):

      tsort <<EOF      
      a b c      
      d      
      e f      
      b c d e      
      EOF      

會産生這樣的輸出

      a      
      b      
      c      
      d      
      e      
      f      

下一次,我們将深入研究

tr

用 tr

過濾檔案

您可以将 tr

看作為 sed

的(極其)簡化的變體:它可以用一個字元來替換另一個字元,或者可以完全除去一些字元。您也可以用它來除去重複字元。這就是所有 tr

所能夠做的。

那麼,為什麼要使用 tr,而不使用

sed 呢?當然是為了使事情簡單。例如,如果我們希望用字母“z”來替換出現的所有字母“a”,則可以用

tr a z

,這條指令毫無疑問比

sed -e s/a/z/g

簡單,尤其在把它用到腳本中時,其中的引号轉義很讓人頭痛。另外,在使用tr

時,可以避免寫那些讓人讨厭的正規表達式。

使用 tr

很簡單:使用前面一段中所給出的符号表示法,用一個字元去替換出現的所有另一個字元。當需要替換多個字元時,使用類似于這樣的表示法:

tr

abc xyz

,它表示用字母“x”去替換出現的所有字母“a”,用字母“y”去替換所有字母“b”,用字母“z”去替換所有字母“c”。這兩組中所列出的字元的數目不必相等。

您也可以指定字元的範圍。例如,

tr a-z A-Z

将用對應的大寫字母來替換所有的小寫字母(例如,它将“nosmoking”轉換成“NOSMOKING”)。當您在vi

編輯器中想強調正在編輯的文本的某一部分時,使用這一特殊技巧非常友善。隻要按一下Escape鍵,然後按

:

鍵,再輸入

2,4!tr 'a-z' 'A-Z'

,最後按一下Return鍵。現在,從第2

行到第 4

行的字母就都轉換成了大寫字母。

另外,當有人給您發送了一個在 Mac OS

或 DOS/Windows

機器上建立的文本檔案時,您會發現 tr非常有用。如果沒有将檔案儲存為使用UNIX

換行符來表示行結束這種格式,則需要将這樣的檔案轉換成本機UNIX格式,否則一些指令實用程式不會正确地處理這些檔案。MacOS的行尾以回車字元結束,許多文本處理工具将這樣的檔案作為一行來處理。為了糾正這個問題,可以用下列技巧:

Mac -> UNIX:

tr '\r' '\n' < macfile > unixfile

UNIX -> Mac:

tr '\n' '\r' < unixfile > macfile

Microsoft DOS/Windows

約定,文本的每行以回車字元并後跟換行符結束。為了糾正這個問題,可以使用下列指令:

DOS -> UNIX:

tr -d '\r' < dosfile > unixfile

UNIX -> DOS:在這種情況下,需要用

awk

,因為 tr 不能插入兩個字元來替換一個字元。要使用的 awk

指令為

awk '{ print $0"\r" }' < unixfile > dosfile

另外,當您需要對文本檔案做一些簡單的整理工作(如用

tr -d '\t'

除去制表符,用

tr -s ' '

除去多餘的空格,或者用

tr -d '\n'

将分開的幾行合成一行)時,會需要用tr。同樣,可以在vi

内使用所有這些指令;隻要記住:在tr指令前要加上您希望處理的行範圍和感歎号(!),如

1,$!tr -d '\t'

(美元符号表示最後一行)中所示。

下次,我們将讨論

uniq

。到那時見!

用 uniq

除去重複行

進行排序之後,您會發現有些行是重複的。有時候該重複資訊是不需要的,可以将它除去以節省磁盤空間。不必對文本行進行排序,但是您應當記住

uniq

在讀取行時會對它們進行比較并将隻除去兩個或更多的連續行。下面的示例說明了它實際上是如何工作的:

清單

1. 用 uniq

$ cat happybirthday.txt

Happy Birthday to You!

Happy Birthday Dear Tux!

$ sort happybirthday.txt

$ sort happybirthday.txt | uniq

警告:請不要使用

或任何其它工具從包含财務或其它重要資料的檔案中除去重複行。在這種情況下,重複行幾乎總是表示同一金額的另一個交易,将它除去會給會計部造成許多困難。千萬别這麼幹!

如果您希望您的工作輕松點,比如隻顯示唯一的或重複的行,那麼該怎麼辦呢?您可以用

-u

(唯一)和

-d

(重複)選項來做到這一點,例如:

2. 使用 -u

和 -d

選項

$ sort happybirthday.txt | uniq -u

$ sort happybirthday.txt | uniq -d

您還可以用

-c

選項從uniq

中擷取一些統計資訊:

3. 使用 -c

$ sort happybirthday.txt | uniq -uc

1 Happy Birthday Dear Tux!

$ sort happybirthday.txt | uniq -dc

3 Happy Birthday to You!

就算

對完整的行進行比較,它仍然會很有用,但是那并非該指令的全部功能。特别友善的是:使用

-f

選項,後面跟着要跳過的字段數,它能夠跳過給定數目的字段。當您檢視系統日志時這非常有用。通常,某些項要被複制許多次,這使得檢視日志很難。使用簡單的

無法完成任務,因為每一項都以不同的時間戳記開頭。但是如果您告訴它跳過所有的時間字段,您的日志一下子就會變得更加便于管理。試一試

uniq -f 3 /var/log/messages,親眼看看。

還有另一個選項

,它的功能就像-f

一樣,但是跳過給定數目的字元。您可以一起使用

和-s

。uniq

先跳過字段,再跳過字元。如果您隻想使用一些預先設定的字元進行比較,那麼該怎麼辦呢?試試看

-w

選項。

下一次,我們将研究

nl

技巧:快速提高Vi/Vim

使用效率的原則與途徑

Vi/Vim

版本的選擇

  “工欲善其事,必先利其器”。在

Vi/Vim 版本的選擇上,原則是“能用

Vim 就不要使用 Vi;能用最新版就不要守着舊版本”。Vim

提供的功能和特性要比 Vi

多得多,如文法加亮着色功能等。就使用效果及效率來說,編輯同樣的檔案,使用Vim

更勝一籌;就版本來說,新版的往往會修複舊版的一些缺陷及不足。這就要求我們在可能的情況下一定要使用最新版的Vim。

小技巧:

  在 Linux

下,如果以 root

使用者登入系統的話,通過 vi

指令打開的 Vim

編輯器往往隻加載最基本的功能,像文法加亮着色的功能基本上沒有。在 root

使用者下使用 Vim

所有功能的技巧是用 vim

指令打開 Vim

編輯器。

回頁首 啟動及關閉 Vi/Vim

  打開及關閉 Vi/Vim

的方法有很多,既可以隻啟動 Vi/Vim

編輯器本身,也可以在啟動Vi/Vim

編輯器的同時打開一個或多個檔案;既可以放棄存盤退出,也可以隻儲存檔案的一部分。以下為相關指令清單:

表 1.

的基本指令

功能

指令

說明

啟動

vi

打開 Vi/Vim

vi <file> 打開 Vi/Vim

并加載檔案 <file>

退出

ZQ

無條件退出

:q! 無條件退出

ZZ 存盤并退出

:wq 存盤并退出

儲存部分檔案

:m,nw <file>

将 m

行到 n

行部分的内容儲存到檔案 <file>

:m,nw >> <file> 将 m

行的内容添加到檔案 <file>

的末尾

  掌握并熟練這些基本指令是使用 Vi/Vim

的基本要求。隻有這樣,才能在實際使用過程中做到按需選擇,靈活使用,提高 Vi/Vim

的使用效率。需要說明的一點是:在使用Vi/Vim

的時候,有時想臨時退出Vi/Vim,轉到shell

環境裡去做一些操作,等這些操作結束後,再繼續回到剛才的Vi/Vim

狀态。針對這一經常碰到的現實需求,很多人在大多數情況下會将儲存退出Vi/Vim,進入shell

執行操作,然後再重新打開剛才編輯的檔案。其實這是一個很低效的方法,因為再次打開需要重新定位剛才編輯的地方,麻煩不說,Vi/Vim編輯器的狀态也完全不一樣了。其實,有兩種方法可以可以實作臨時退出Vi/Vim、進入shell

環境後再回來的要求:

方法一:使用 Ctrl-z

以及 fg

這兩個指令組合。

  這一解決方法主要利用了 Linux/Unix

的作業機制。具體原理是:Ctrl-z

指令将目前的 Vi/Vim

程序放到背景執行,之後shell

環境即可為你所用;fg指令則将位于背景的Vi/Vim

程序放到前台執行,這樣我們就再次進入Vi/Vim

操作界面并恢複到原先的編輯狀态。

方法二:使用行指令 :sh。

  在 Vi/Vim

的正常模式下輸入

:sh即可進入Linux/Unix shell

環境。在要傳回到 Vi/Vim

編輯環境時,輸入 exit

指令即可。

  這兩種方法實作機制不一定,但效果一樣,都非常快捷有效。

移動光标

   Vi/Vim

中關于光标移動的指令非常多,這也是很多人經常困惑并且指令用不好的地方之一。其實 Vi/Vim

中很多指令是針對不同的操作機關而設的,不同的指令對應不同的操作機關。因而,在使用指令進行操作的時候,首先要搞清楚的就是要采用哪種操作機關,也就是說,是要操作一個字元,一個句子,一個段落,還是要操作一行,一屏、一頁。機關不同,指令也就不同。隻要機關選用得當,指令自然就恰當,操作也自然迅速高效;否則,隻能是費時費力。這也可以說是最能展現Vi/Vim

優越于其它編輯器的地方之一,也是 Vi/Vim

有人愛有人恨的地方之一。在操作機關确定之後,才是操作次數,即确定指令重複執行的次數。要正确高效的運用 Vi/Vim

的各種操作,一定要把握這一原則:先定機關再定量。操作對象的範圍計算公式為:操作範圍

= 操作次數 *

操作機關。比如:5h

指令左移 5

個字元,8w

指令右移 8

個單詞。

  

注:有些操作機關(如檔案)是不能加操作次數。具體說明請參考Vi/Vim

使用手冊。

中操作機關有很多,按從小到大的順序為(括号内為相應的操作指令):字元(h、l)→

單詞 (w、W、b、B、e)

→ 行 (j、k、0、^、$、:n)

→ 句子((、))→

段落({、})→

屏 (H、M、L)

→ 頁(Ctrl-f、Ctrl-b、Ctrl-u、Ctrl-d)

→ 檔案(G、gg、:0、:$)。

  具體指令解釋如下:

表 2.

移動光标的基本指令

操作機關

字元

h

左移一字元

l 右移一字元

單詞

w/W

移動到下一單詞的開頭

b/B 移動到上一單詞的開頭

e/E 移動到光标所在單詞的末尾

W、B、E

指令操作的單詞是以空白字元(空格、Tab)分隔的字串,比如字元串“str1-str2

str3-str4”,對 W、B、E

指令來說是兩個單詞,而對 w、b、e

指令來說則是四個單詞。

j

下移一行

k 上移一行

0 移到目前行開頭

^ 移到目前行的第一個非空字元

$ 移到目前行末尾

:n 移動到第 n

為數字零(zero)

句子

)

移動到目前句子的末尾

( 移動到目前句子的開頭

段落

}

移動目前段落的末尾

{ 移到目前段落的開頭

H

移動到螢幕的第一行

M 移動到螢幕的中間一行

L 移動到螢幕的最後一行

Ctrl-f

向前滾動一頁

Ctrl-b 向後滾動一頁

Ctrl-u 向前滾動半頁

Ctrl-d 向後滾動半頁

檔案

G

移動到檔案末尾

gg 移動到檔案開頭

:0 移動到檔案第一行

:$ 移動到檔案最後一行

  除了這些基本機關之外,還有

%(跳轉到與之比對的括号處),`.(跳轉到最近修改過的位置并定位編輯點),'.(跳轉到最近修改過的位置但不定位編輯點)這三個指令也非常重要,在Vi/Vim

中靈活使用會極大提高效率。%除用于光标移動之後,還可用于檢測源碼中各種括号的比對情況。

文本編輯

  與光标移動一樣,Vi/Vim

中關于編輯操作的指令也比較多,但操作機關要比移動光标少得多。按從小到大的順序為(括号内為相應的操作指令):字元

(x、c、s、r、i、a)→

單詞 (cw、cW、cb、cB、dw、dW、db、dB)

→ 行 (dd、d0、d$、I、A、o、O)

段落({、})。這些操作機關有些可以加操作次數。操作對象的範圍計算公式為:操作範圍

= 操作次數*

操作機關。比如:d3w

指令删除三個單詞,10dd指令删除十行。

表 3.

文本編輯的基本指令

x

删除光标位置的字元

c 更改目前字元并進入插入模式

s 替換光标位置的字元并進入插入模式

r 替換光标位置的字元但不進入插入模式

i 在目前位置的字元之前進入插入模式

a 在目前位置的字元之後進入插入模式

cw/cW

删除目前單詞從光标開始的部分并進入插入模式

cb/cB 删除目前單詞從光标所在位置至單詞開始的部分并進入插入模式

dw/dW 删除目前單詞從光标開始的部分但不進入插入模式

db/dB 删除目前單詞從光标所在位置至單詞開始的部分但不進入插入模式

cW、cB、dW、dB

str3-str4”,對 cW,cB,dW,dB

指令來說是兩個單詞,而對 cw、cb、dw、db

dd

删除目前行

d0 删除從目前光标開始到行首的内容

d$ 删除從目前光标開始到行末的内容

I 在目前行的行首進入插入模式

A 在目前行的行尾進入插入模式

o 在目前行下方另起一行進入插入模式

O 在目前行上方另起一行進入插入模式

d0 指令中的 0

為數字零

o 為小寫英文字母 [əu]

O 為大寫英文字母 [əu]

d)

删除目前句子從光标位置開始到句末的内容

d( 删除目前句子從光标位置開始到句首的内容

d}

删除目前段落從光标位置開始到段末的内容

d{ 删除目前段落從光标位置開始到段首的内容

  除上述最基本的文本編輯指令這外,Vi/Vim

還提供了許多其它的編輯指令或相關組合。使用這些指令或相關組合往往在極大提高文本編輯的效率與速度。現将這些指令按功能列如表下:

表 4.

文本編輯的高效指令

複制與粘貼

yw

複制目前單詞從光标開始的部分

yy 複制光标所在行的所有字元

p 将最後一個删除或複制文本放在目前字元

P 将最後一個删除或複制文本放在目前字元之前

配合操作數使用可快速拷貝編輯文本

撤消與重做

u

撤消更改

Ctrl-R 重做更改

非常實用的一個指令

重複操作

.

重複上次操作

. 為小數點(dot)

配合光标移動指令使用;

不用重複輸入先前的複雜指令即可在不同的地方做同樣的操作,有點象 MS Office

的格式刷

交換相鄰字元或行

xp

交換光标位置的字元和它右邊的字元

ddp 交換光标位置的行和它的下一行

大小寫轉換

~

将光标下的字母大小寫反向轉換

guw 将光标所在的單詞變為小寫

gUw 将光标所在的單詞變為大寫

guu 光标所在的行所有字元變為小寫

gUU 光标所在的行所有字元變為大寫

g~~ 光标所在的行所有字元大小寫反向轉換

取得外部輸入

: r!<cmd>

将指令 <cmd>

的輸出結果插入到目前光标所在位置

:r <file> 将檔案 <file>

讀入到目前光标所在位置

排序

:1,$!sort

将檔案内的所有内容排序

加入行号

:%!nl

在所有非空行前加入行号

:%!nl -ba 在所有行前加入行号

利用 Linux

指令 nl

來實作的

縮進

>>

右縮進(可配合操作數使用)

<< 左縮進(可配合操作數使用)

配合操作數使用,在編輯源碼的時候非常有用。

自動補全

Ctrl-p

在編寫代碼的時候非常有用。比如,輸入 prin

後按 Ctrl-p

将自動幫你輸入 printf

函數名後面的部分,同時将相關備選函數在底下列出來。

顯示目前編輯檔案名

Ctrl-g

顯示目前編輯檔案名及行數

可以在不退出 Vi/Vim

情況下了解目前編輯檔案的資訊

顯示字元内碼

ga

顯示光标所在字元的内碼(包括十進制碼,十六進制碼以及八進制碼)

顯示的内碼為目前 encoding

下的内碼

文本搜尋與替換

用于文本搜尋的主要有下面的三個基本指令:

表 5.

文本搜尋的基本指令

搜尋

/

在檔案中向前搜尋

? 在檔案中向後搜尋

可使用正規表達式

可配合操作數使用,比如 3/str

向前搜尋字串 str

并将光标移到第三個找到的串

搜尋下一個

n

N 反向搜尋下一個

可配合操作數使用,比如指令 3N

反向搜尋第三個比對的字元串

  除這三個基本指令之外,還有以下三個非常有效快捷的與搜尋查找有關的指令:

表 6.

文本搜尋的高效指令

快速搜尋

*

在檔案中向前搜尋目前光标所在的單詞

# 在檔案中向後搜尋目前光标所在的單詞

非常快捷的搜尋指令

顯示搜尋指令曆史

q/

顯示搜尋指令曆史的視窗

q? 顯示搜尋指令曆史的視窗

可以選擇重用以前用過的搜尋查找指令

查找幫助

Shift-k

查找光标所在指令或函數的 man

幫助

情況下快速查詢指令或函數的使用方法;

按 q

鍵退出 man

  關于替換主是要結合搜尋使用行指令來實作,指令格式為:

    

:m,ns/str1/str2/g将 m

行中的字串 str1

全部替換為字串 str2。

  在衆多使用正規表達式進行替換的指令中,平時需要掌握的一個指令是如何快速去除 ^M

字元。在 Linux/Unix

系統中編輯 Windows

作業系統中生成的檔案時,如果上傳時回車換行符處理不正确的話,用Vi/Vim

打開經常會出現^M

字元。如果上傳的檔案是shell

腳本的話,即使賦予了正确的執行權限該腳本還是無法運作。這也是各種檔案在Linux/Unix

和Windows

中傳輸經常會出現問題的地方之一。其實,在Vi/Vim

中使用替換指令:1,$s/^M//g即可以很容易地快速去掉

^M 字元。這裡需要注意的是 ^M

是使用 Ctrl-v、Ctrl-m

輸入的。

執行外部指令

中還可以在正常模式下執行各種外部指令,指令格式如下:

表 7.

執行外部指令格式

:!<cmd>

執行外部指令 <cmd>

在正常模式下輸入該指令

顯示指令行指令曆史

q:

顯示指令行指令曆史的視窗

可以選擇重用以前用過的指令行指令

  其中指令

q:會顯示使用過的行指令曆史,可以從中選擇重用以前用過的指令。這對于需要重複應用那些複雜的指令來說,非常友善快捷。

設定

有很多内部變量,可以根據需要進行相應的設定。變量類型不同往往設定方式也不一樣,簡單的隻要設定特定的變量名即可,複雜的則需要指定和配置設定一個顯式值來設定變量。在實際應用中,如果有需要,請參考Vi/Vim

的使用手冊。這裡主要列出大家經常使用并能提高編輯效率的一些設定指令:

表 8. Vi/Vim

設定指令

檢視設定的目前值

:set all

檢視 vi

或 Vim

中設定的所有選項的目前值

:set <option>? 檢視特定選項 <option>

的目前值

設定行号顯示與否

:set number

顯示行号

:set no number 取消行号顯示

指令的簡寫形式:

:set nu

:set no nu

設定自動縮進

:set autoindent

:set no autoindent 取消自動縮進設定

:set ai

:set no ai

設定縮進寬度

:set shiftwidth=4

設定縮進寬度為 4

:set sw=4

設定大小寫忽略與否

:set ignorecase

設定忽略大小

:set no ignorecase 取消忽略大小設定

:set ic

:set no ic

設定不可見字元顯示與否

:set list

顯示不可見字元

:set nolist 取消顯示不可見字元設定

在顯示不可見字元的情況下,TAB

鍵顯示為 ^I,而

$ 顯示在每行的結尾。

  在正常模式 (Normal mode)

時,執行這些設定指令隻修改目前會話的設定,退出或重新開機 Vi/Vim

這些設定就丢失了。要想保持住這些設定,就必須将這些設定寫入Vi/Vim

的vimrc

檔案。對Linux/Unix

作業系統來說,每個使用者的vimrc

檔案位于該使用者的主目錄下,檔案名為.vimrc。Vi/Vim在每次啟動的時候都會讀取使用者主目錄下的vimrc

檔案并據此設定Vi/Vim

的使用環境。Vi/Vim在安裝的同時也會安裝vimrc

檔案的一個示例vimrc_example.vim

到 /usr/share/vim/vim<version>

目錄下。可以根據需要将這個示例檔案拷貝到目前使用者的主目錄下并重命名為.vimrc,在此基礎上進行修改會相對容易一些。其它關于Vi/Vim

的一些定制和鍵映射等相關設定基本上也是寫入使用者的vimrc

檔案中,更詳細的介紹請參考Vi/Vim

的使用手冊。使用者也可以維護一個自己的vimrc

檔案,并将這個檔案拷貝到自己的使用的環境中,保持不同環境中Vi/Vim

特性的一緻,以符合自己的使用習慣。

檔案和目錄管理

列出目錄

Linux

和 UNIX®

系統中的所有檔案都可以作為一個大型樹型檔案系統的一部分通路,這個樹型檔案系統的根為 /。通過挂載

分支可以将它們添加到樹中,通過解除挂載

可以移除它們。挂載和解除挂載的内容将在挂載和解除挂載檔案系統

一文介紹。

(參見

學習

Linux,101:LPIC-1

路線圖

列出目錄條目

在本文中,我們将使用 “

Linux,101:文本流和過濾器

一文中建立的檔案來練習指令。如果您完成了上篇文章的練習,那麼您應該在您的主目錄中建立了一個目錄lpi103-2。如果還沒有的話,那麼可以使用系統中的另一個目錄來練習本文讨論的指令。

檔案和目錄名可以是絕對

的,這表示名稱以 /

開頭,也可以相對

于目前工作目錄,這表示不是以/

開頭。檔案或目錄的絕對路徑的組成為:在0

個或多個目錄名後附加一個/,其中每個目錄名的後面都有一個/,然後是一個最終檔案名。

至于相對于目前工作目錄的檔案或目錄名,隻需要将工作目錄的絕對名、/

和相對名連接配接在一起。例如,我們在早期文章中在我的主目錄 /home/ian

中建立的目錄lpi103-2,它的完整(即絕對)路徑為/home/ian/lpi103-2。

pwd

指令顯示目前工作目錄的名稱。此指令通常也可以用于PWD

環境變量。清單1

展示了

pwd

指令的使用,以及通過三種不同的方法使用

ls

指令列出此目錄中的檔案。

1. 列出目錄條目

[ian@echidna lpi103-2]$ pwd      
/home/ian/lpi103-2      
[ian@echidna lpi103-2]$ echo "$PWD"      
/home/ian/lpi103-2      
[ian@echidna lpi103-2]$ ls      
sedtab  text1  text2  text3  text4  text5  text6  xaa  xab  yaa  yab      
[ian@echidna lpi103-2]$ ls "$PWD"      
sedtab  text1  text2  text3  text4  text5  text6  xaa  xab  yaa  yab      
[ian@echidna lpi103-2]$ ls /home/ian/lpi103-2      
sedtab  text1  text2  text3  text4  text5  text6  xaa  xab  yaa  yab      

可以看到,您可以将一個相對或絕對目錄名作為

ls

目錄的參數,它将列出該目錄中的内容。

列出詳細内容

在一台儲存設備中,檔案或目錄被包含到一個塊(block)組合中。有關檔案的資訊被包含在一個索引節點(inode)中,其中記錄如下資訊:所有者、最後一次通路檔案的時間、檔案大小、是否為目錄以及誰可以讀取或寫入資料。inode編号也被稱為檔案序列号(fileserial

number),并且在一個特定檔案系統中是唯一的。我們可以使用

-l

(或

--format=long

)選項來顯示存儲在 inode

中的某些資訊。

預設情況下,

ls

指令不會列出特殊檔案,這些檔案的檔案名以點号(.)

開頭。除根目錄外的所有目錄都至少包含兩個特殊條目:目錄本身(.)

和父目錄(..)。根目錄沒有父目錄。

清單 2

使用

-l

-a

選項顯示所有檔案的長格式的清單,包括.

和 ..

目錄條目。

2. 顯示一個長目錄清單

[ian@echidna lpi103-2]$ ls -al      
total 52      
drwxrwxr-x.  2 ian ian 4096 2009-08-11 21:21 .      
drwx------. 35 ian ian 4096 2009-08-12 10:55 ..      
-rw-rw-r--.  1 ian ian    8 2009-08-11 21:17 sedtab      
-rw-rw-r--.  1 ian ian   24 2009-08-11 14:02 text1      
-rw-rw-r--.  1 ian ian   25 2009-08-11 14:27 text2      
-rw-rw-r--.  1 ian ian   63 2009-08-11 15:41 text3      
-rw-rw-r--.  1 ian ian   26 2009-08-11 15:42 text4      
-rw-rw-r--.  1 ian ian   24 2009-08-11 18:47 text5      
-rw-rw-r--.  1 ian ian   98 2009-08-11 21:21 text6      
-rw-rw-r--.  1 ian ian   15 2009-08-11 14:41 xaa      
-rw-rw-r--.  1 ian ian    9 2009-08-11 14:41 xab      
-rw-rw-r--.  1 ian ian   17 2009-08-11 14:41 yaa      
-rw-rw-r--.  1 ian ian    8 2009-08-11 14:41 yab      

在清單 2

中,第一行顯示所列檔案使用的磁盤塊的總數(52)。其餘行列出了目錄的條目。

第一個字段(本例中為 drwxrwxr-x

或 -rw-rw-r--)告訴我們,檔案是一個目錄 (d)

還是一個普通檔案 (-)。對于特殊檔案,還會看到符号連結 (l)

或其他值(例如 /dev

檔案系統中的檔案)。您将在建立和修改硬連結和符号連結

一文(參見

學習 Linux,101:LPIC-1

)中了解到有關符号連結的更多内容。類型之後是針對所有者、所有者所在組的成員、每一個成員的三組特權。這三個值分别表示使用者、組、組成員是否擁有讀 (r)、寫

(w) 或 (x)

執行權限。諸如 setuid 之類的使用者将在管理檔案權限和所有權(參見

)一文中介紹。

下一個字段是一個數字,告訴我們檔案的硬連結

的數量。我們已經介紹過,inode

包含有關檔案的資訊。檔案的目錄條目包含到檔案的 inode 的硬連結(或指針),是以列出的每個條目都應該至少擁有一個硬連結。目錄條目對 .

條目和每個子目錄條目使用另外的硬連結。是以我們可以從清單 2

中可以看到,使用 .. 表示的主目錄有大量子目錄,是以包含 35

個硬連結。

接下來兩個字段分别為檔案的所有者和所有者的主組。某些系統,例如 Red Hat

或 Fedora

系統,在預設情況下為每個使用者提供單獨的組。在其他系統中,所有使用者可能位于一個或多個組中。

下一個字段包含檔案的長度,以位元組為機關。

倒數第二個字段包含最後一次修改的時間戳。

最後一個字段包含檔案或目錄的名稱。

ls

指令的

-i

選項将顯示 inode

号。您将在本文後面以及

建立和修改硬連結和符号連結(參見

)中再次見到有關

inode的介紹。

多個檔案

您還可以為

ls

指令指定多個參數,其中的每個名稱都可能是檔案或目錄的名稱。對于目錄名,

ls

指令将列出目錄的内容,而不是關于目錄本身的資訊。在我們的示例中,假設當在父目錄中列出目錄時,我們希望獲得有關lpi103-2

目錄條目本身的資訊。指令

ls -l ../lpi103-2

将提供類似前例的清單。清單3

将展示如何添加

-d

選項以列出有關目錄條目的資訊,而不是目錄的内容,以及如何列出多個檔案或目錄的條目。

3. 使用

ls -d

[ian@echidna lpi103-2]$ ls -ld ../lpi103-2 sedtab xaa      
drwxrwxr-x. 2 ian ian 4096 2009-08-12 15:31 ../lpi103-2      
-rw-rw-r--. 1 ian ian    8 2009-08-11 21:17 sedtab      
-rw-rw-r--. 1 ian ian   15 2009-08-11 14:41 xaa      

注意,lpi103-2

的修改時間不同于前一個清單中的修改時間。同樣,和前一個清單相同,它與該目錄中的任何檔案的時間戳都不同。這是否就是您所期望的?并不是這樣。然而,在撰寫本文時,我建立了一些額外的的例子并删除了它們,是以目錄時間戳反映了這一更改。稍後在

處理多個檔案和目錄

中,我們将更詳細地讨論檔案時間。

對輸出排序

ls

将按字母順序列出檔案。可以使用多種選項對輸出進行排序。例如,

ls -t

将按照修改時間排序(從最新到最舊),而

ls -lS

将生成一個按大小排序的長清單(從最大到最小)。添加

-r

将反向排序。例如,使用

ls -lrt

生成一個按從最舊到最新排序的長清單。參考手冊頁面,了解有關排列檔案和目錄的其他方式。

複制、移動和删除檔案

我們現在已經了解了一些建立檔案的方法,但是假設我們希望複制檔案、重命名檔案、在檔案系統層級結構中移動檔案,甚至删除它們。我們使用三個簡短的指令來實作這些目的。

cp

用于複制一個或多個檔案或目錄。您必須提供一個(或多個)源

名和一個目标

名。源名或目标名可能包含一個路徑說明。如果目标是一個現有目錄,那麼所有源将被複制到目标中。如果目錄是一個不存在的目錄,那麼(單一)源也必須為一個目錄,并且源目錄的副本及其内容使用目标名作為新名稱。如果目标是一個檔案,那麼(單一)源必須也為檔案,而源檔案的副本使用目标名作為新名,替換任何現有的具有相同名稱的檔案。注意,在DOS

作業系統中,不會做出目标為目前目錄的預設假設。

mv

用于移動

或重命名

一個或多個檔案或目錄。一般來說,您使用的名稱将遵守與

cp

相同的規則;您可以重命名某個檔案或将一組檔案移動到一個新目錄中。由于名稱隻是一個連結到某個inode

的目錄條目,是以inode

号隻有在檔案被移動到另一個檔案系統才會發生更改就不足為怪了,在這種情況下,移動檔案看上去就類似于在複制檔案之後删除它。

rm

用于删除

一個或多個檔案。我們後面将介紹如何删除目錄。

rename

指令在哪裡?

如果您熟悉 DOS

或 Windows®

系統,您會發現使用

mv

重命名檔案有點怪異。Linux确實提供了

rename

指令,但是該指令與 DOS

和 Windows

下相同名稱的指令具有不同的文法。檢視手冊頁,了解該指令的使用詳情。

清單 4

示範了

cp

mv

的使用,它們對我們的文本檔案執行了一些備份複制。我們使用

ls -i

展示其中一些檔案的inode。

我們首先為 text1

檔案生成一個副本 text1.bkp。

然後決定使用

mkdir

指令建立一個備份子目錄

我們為文本 1

生成第二個備份副本,這一次是在備份目錄中,并顯示出所有三個檔案都具有不同的 inode。

随後将 text1.bkp

移動到備份目錄中,然後将其重命名,使其與第二個備份更加一緻。我們本來可以使用一個單個指令完成這些操作,但是為了示範的目的,我們在這裡使用了兩個指令。

我們再次檢查 inode,然後确定 inode

為 934193

的 text1.bkp 不再存在于 lpi103-2

目錄,但是該 inode

仍然為備份目錄中的 text1.bkp.1 保留下來。

4. 複制和移動檔案

[ian@echidna lpi103-2]$ cp text1 text1.bkp      
[ian@echidna lpi103-2]$ mkdir backup      
[ian@echidna lpi103-2]$ cp text1 backup/text1.bkp.2      
[ian@echidna lpi103-2]$ ls -i text1 text1.bkp backup      
933892 text1  934193 text1.bkp      
backup:      
934195 text1.bkp.2      
[ian@echidna lpi103-2]$ mv text1.bkp backup      
[ian@echidna lpi103-2]$ mv backup/text1.bkp backup/text1.bkp.1      
[ian@echidna lpi103-2]$ ls -i text1 text1.bkp backup      
ls: cannot access text1.bkp: No such file or directory      
933892 text1      
backup:      
934193 text1.bkp.1  934195 text1.bkp.2      

一般來說,

cp

将在現有副本上複制檔案,如果現有檔案可寫的話。另一方面,如果目标存在,

mv

不會移動或重命名檔案。有一些有用的選項與

cp

mv

的這種行為有關。

-f

--force

将促使

cp

嘗試闡釋一個現有目标檔案,即使它不是可寫的

-i

--interactive

将要求在嘗試替換某個現有檔案之前進行确認

-b

--backup

将為即将被替換的任何檔案生成備份

和前面一樣,您需要參考手冊頁來獲得這些和其他複制和移動選項的詳細内容。

清單 5

示範了備份複制和檔案删除。

5. 生成備份副本并删除檔案

[ian@echidna lpi103-2]$ cp text2 backup      
[ian@echidna lpi103-2]$ cp --backup=t text2 backup      
[ian@echidna lpi103-2]$ ls backup      
text1.bkp.1  text1.bkp.2  text2  text2.~1~      
[ian@echidna lpi103-2]$ rm backup/text2 backup/text2.~1~      
[ian@echidna lpi103-2]$ ls backup      
text1.bkp.1  text1.bkp.2      

注意,

rm

指令還接受

-i

(互動式)和

-f

(強制選項)。當您使用

rm

删除檔案後,檔案系統将不再通路它。某些系統在預設情況下為根使用者設定一個别名

alias rm='rm -i'

,以防止出現意外的檔案删除。如果您擔心會不小心删除檔案的話,這對于普通使用者來說也是一個好主意。

在結束這些内容的讨論之前,應當注意

cp

指令在預設情況下會為新的檔案建立一個新的時間戳。所有者群組均被設定為執行複制的使用者的所有者群組。

-p

選項可能被用于儲存選擇的屬性。注意,根使用者可能為可以保留所有權的唯一使用者。參考手冊頁獲得詳情。

建立和删除目錄

我們已經了解了如何使用

mkdir

建立目錄。現在我們将進一步檢視

mkdir

并介紹

rmdir

,後者用于删除目錄。

Mkdir

假設我們希望在 lpi103-2

目錄中建立子目錄 dir1

和 dir2。和前面介紹過的其他指令一樣,

mkdir

可以一次處理多個目錄建立請求,如清單6

所示。

6. 建立多個目錄

[ian@echidna lpi103-2]$ mkdir dir1 dir2      

注意,在成功完成後不會産生輸出,但是您可以使用

echo $?

來确認退出代碼确實為 0。

相反,如果您希望建立一個嵌入式的子目錄,比如 d1/d2/d3,那麼指令将會失敗,因為

d1 和 d2

目錄并不存在。幸運的是,

mkdir

具有一個

-p

選項,它允許建立任何所需的父目錄,如清單 7

7. 建立父目錄

[ian@echidna lpi103-2]$ mkdir d1/d2/d3      
mkdir: cannot create directory `d1/d2/d3': No such file or directory      
[ian@echidna lpi103-2]$ echo $?      
1      
[ian@echidna lpi103-2]$ mkdir -p d1/d2/d3      
[ian@echidna lpi103-2]$ echo $?      
Rmdir

rmdir

指令删除目錄正好與建立過程相反。同樣,可以用

-p

選項來删除父目錄。隻有在目錄為空的情況下才可以使用

rmdir

删除目錄,因為不存在可以強制删除的選項。我們将在讨論

遞歸操作

時檢視另一種可以完成這一特殊任務的方法。了解了這種方法後,您将很少會在指令行中使用

rmdir

,但是了解該指令仍然是有用的。

為了解釋目錄删除,我們将 text1

檔案複制到目錄 d1/d2

中,這樣它就不會成為空目錄。我們随後使用

rmdir

來删除剛剛用

mkdir

建立的所有目錄。可以看到,d1和

d2 沒有被删除,因為 d2

不為空。另一個目錄則被删除,當我們從d2

删除text1

的副本時,我們隻需要調用

rmdir -p

即可删除 d1

和 d2。

8. 删除目錄

[ian@echidna lpi103-2]$ cp text1 d1/d2      
[ian@echidna lpi103-2]$ rmdir -p d1/d2/d3 dir1 dir2      
rmdir: failed to remove directory `d1/d2': Directory not empty      
[ian@echidna lpi103-2]$ ls . d1/d2      
.:      
backup  sedtab  text2  text4  text6  xab  yab      
d1      text1   text3  text5  xaa    yaa      
d1/d2:      
text1      
[ian@echidna lpi103-2]$ rm d1/d2/text1      
[ian@echidna lpi103-2]$ rmdir -p d1/d2      

到目前為止,我們使用的指令已經處理了一個單個檔案,或者一些個别命名的檔案。在本文的其餘部分中,我們将檢視處理多個檔案的各種操作,遞歸式處理檔案樹的某一部分,儲存或恢複多個檔案或目錄。

遞歸式操作 遞歸式清單

ls

指令有一個

-R

(注意為大寫 “R”)選項,可以列出一個目錄及其所有子目錄。遞歸式操作隻能應用于目錄名;它不會在目錄樹中查找名為‘text1’

之類的檔案。您可以将

-R

與已經介紹過的其他選項結合使用。lpi103-2目錄的遞歸式清單包括inode

号,如清單9

9. 遞歸式顯示目錄清單

[ian@echidna lpi103-2]$ ls -iR      
.:      
934194 backup  933892 text1  933898 text3  933900 text5  933894 xaa  933896 yaa      
933901 sedtab  933893 text2  933899 text4  933902 text6  933895 xab  933897 yab      
./backup:      
934193 text1.bkp.1  934195 text1.bkp.2      
遞歸式複制

可以使用

-r

-R

--recursive

)選項來使

cp

指令進入到源目錄并以遞歸的方式複制目錄。為了防止出現無窮遞歸,源目錄本身可能不會被複制。清單10

展示了如何将lpi103-2

目錄中的所有内容複制到copy1

子目錄。我們使用

ls -R

展示生成的目錄樹。

10. 遞歸複制

[ian@echidna lpi103-2]$ cp -pR . copy1      
cp: cannot copy a directory, `.', into itself, `copy1'      
[ian@echidna lpi103-2]$ ls -R      
.:      
backup  copy1  sedtab  text1  text2  text3  text4  text5  text6  xaa  xab  yaa  yab      
./backup:      
text1.bkp.1  text1.bkp.2      
./copy1:      
text2  text3  text5  xaa  yaa  yab      
遞歸删除

我們前面提到,

rmdir

隻能删除空目錄。我們可以使用

-r

-R

--recursive

rm

指令同時删除檔案和目錄,如清單11

所示,我們将删除剛剛建立的copy1

目錄和它包含的内容,包括備份子目錄及其内容。

11. 遞歸式删除

[ian@echidna lpi103-2]$ rm -r copy1      
[ian@echidna lpi103-2]$ ls -R      
.:      
backup  sedtab  text1  text2  text3  text4  text5  text6  xaa  xab  yaa  yab      
./backup:      
text1.bkp.1  text1.bkp.2      

如果您具有不可寫的檔案,那麼可能需要添加

-f

選項來強制删除。這通常由根使用者在清理系統時執行,但是會發出警告,因為您有可能會不小心删除重要的資料。

通配符和 globbing

通常,您需要對多個檔案系統對象執行單一操作,而不需要像前面的遞歸操作一樣對整個樹進行操作。例如,您可能想要找出在 lpi103-2

中建立的所有文本檔案的修改時間,而不需要列出分散的檔案。盡管這很容易在小目錄中實作,但是對于大型檔案系統則非常困難。

要解決這個問題,可以使用 bash shell

中内置的通配符支援。這種支援也稱為 “globbing”(因為它最初被實作為一個名為

/etc/glob 的程式),讓您能夠使用通配符模式指定多個檔案。

包含任何 '?'、'*'

或 '['

字元的字元串就是一個通配符模式。Globbing是指

shell(或另一個程式)将這些模式擴充為一組比對該模式的參數的過程。這種比對按照如下方式完成:

?

比對任何單個字讀。

比對任何字元串,包括空字元串。

[

引入了一個字元類(character

class)。字元類是一個非空字元串,以']'

結尾。比對意味着需要與方括号中包括的任何單個字元相一緻。這裡需要考慮一些特殊的事項。

  • '*' 和 '?'

    字元與它們自身比對。如果在檔案名中使用這些字元,那麼需要注意适當的引用或轉義。

  • 由于字元串必須是非空的并以 ']'

    終止,如果您需要比對字元串的話,您必須将 ']' 放到字元串的首位。

  • 兩個字元之間的 '-'

    字元表示一個範圍,包括這兩個字元和排序序列中介于這兩個字元之間的所有字元。例如,[0-9a-fA-F]表示任何大寫或小寫十六進制數位。您可以通過将'-'

    放到一個範圍的首位或末位來比對它。

  • 如果範圍的首個字元為 '!'

    字元,那麼它将對範圍求餘,即它将比對剩餘字元以外的所有字元。例如,[!0-9]表示除0

    到9

    之間數字的任何字元。将'!' 放在首位以外的任意位置都可以比對它本身。注意'!'

    也可以用于shell history

    函數,是以需要小心地對它進行适當的轉義。

注意:通配符模式和正常表達式模式具有一些共同點,但是它們是不同的。需要仔細留意。

Globbing

被單獨應用到路徑名的每個組成中。您無法比對 '/',也不能把它包含在一個範圍中。您可以在指定多個檔案或目錄名時使用它,例如在

ls

cp

mv

rm

指令中。在清單 12

中,我們首先建立一對名字奇怪的檔案,然後對通配符模式使用

ls

rm

指令。

12. 通配符模式示例

[ian@echidna lpi103-2]$ echo odd1>'text[*?!1]'      
[ian@echidna lpi103-2]$ echo odd2>'text[2*?!]'      
[ian@echidna lpi103-2]$ ls      
backup  text1       text2       text3  text5  xaa  yaa      
sedtab  text[*?!1]  text[2*?!]  text4  text6  xab  yab      
[ian@echidna lpi103-2]$ ls text[2-4]      
text2  text3  text4      
[ian@echidna lpi103-2]$ ls text[!2-4]      
text1  text5  text6      
[ian@echidna lpi103-2]$ ls text*[2-4]*      
text2  text[2*?!]  text3  text4      
[ian@echidna lpi103-2]$ ls text*[!2-4]* # Surprise!      
text1  text[*?!1]  text[2*?!]  text5  text6      
[ian@echidna lpi103-2]$ ls text*[!2-4] # Another surprise!      
text1  text[*?!1]  text[2*?!]  text5  text6      
[ian@echidna lpi103-2]$ echo text*>text10      
[ian@echidna lpi103-2]$ ls *\!*      
text[*?!1]  text[2*?!]      
[ian@echidna lpi103-2]$ ls *[x\!]*      
text1  text[*?!1]  text10  text2  text[2*?!]  text3  text4  text5  text6  xaa  xab      
[ian@echidna lpi103-2]$ ls *[y\!]*      
text[*?!1]  text[2*?!]  yaa  yab      
[ian@echidna lpi103-2]$ ls tex?[[]*      
text[*?!1]  text[2*?!]      
[ian@echidna lpi103-2]$ rm tex?[[]*      
[ian@echidna lpi103-2]$ ls *b*      
sedtab  xab  yab      
backup:      
text1.bkp.1  text1.bkp.2      
[ian@echidna lpi103-2]$ ls backup/*2      
backup/text1.bkp.2      
[ian@echidna lpi103-2]$ ls -d .*      
.  ..      

注意:

結合使用 '*'

會出現一些意外。模式 '*[!2-4]' 比對名稱中不包含 2、3

或 4

的最長的一部分,這部分可以同時被 text[*?!1]

和 text[2*?!]

比對。現在所有這些意外都應該清楚了。

和前面的

ls

示例一樣,如果模式擴充導緻一個目錄名,并且沒有指定

-d

選項,那麼該目錄的内容将被列出(和前例中的模式 '*b*' 一樣)。

如果一個檔案名以句點 (.)

開頭,那麼該字元必須被明确比對。注意,隻有最後一個

ls

指令列出兩個特殊的目錄條目(.

和 ..)。

請注意,指令中的任何通配符都可以被 shell

擴充,這将導緻意外的結果。并且,如果您指定一個不比對任何檔案系統對象的模式,那麼 POSIX

要求原始模式字元串被傳遞給指令。一些早期的實作将一個null

清單傳遞給指令,是以您可能會遇到一些表現出異常行為的老腳本。我們将在清單13

中解釋這些内容。

13. 通配符模式異常

[ian@echidna lpi103-2]$ echo text*      
text1 text10 text2 text3 text4 text5 text6      
[ian@echidna lpi103-2]$ echo "text*"      
text*      
[ian@echidna lpi103-2]$ echo text[[\!?]z??      
text[[!?]z??      

有關 globbing

的更多資訊,請查閱

man7 glob

。您将需要章節号,因為第 3

節中也介紹了 glob

資訊。了解所有不同 shell

互動的最佳方式是進行實踐,是以您需要多多嘗試這些通配符。注意,在使用

cp

mv

rm

出現異常行為之前,使用

ls

檢查您的通配符模式。

對檔案執行 Touch

我們現在來看看

touch

指令,它将更新檔案通路和修改時間或建立空檔案。在下一部分中,我們将探讨如何使用這些資訊查找檔案和目錄。我們将繼續在示例中使用lpi103-2

目錄。我們還将檢視各種指定時間戳的方法。

touch

不包含任何選項的

touch

指令使用一個或多個檔案名作為參數,并将更新檔案的修改時間。這個時間戳通常會和一個長目錄清單一同顯示。在清單14

中,我們使用

echo

建立了一個小檔案 f1,然後使用一個長目錄清單來顯示修改時間(即

mtime)。在本例中,修改時間正好是檔案的建立時間。我們随後使用

sleep

指令來等待 60

秒,并在此運作

ls

。注意,該檔案的時間戳已經修改了一段時間。

14. 使用

touch 更新修改時間

[ian@echidna lpi103-2]$ echo xxx>f1; ls -l f1; sleep 60; touch f1; ls -l f1      
-rw-rw-r--. 1 ian ian 4 2009-08-14 18:24 f1      
-rw-rw-r--. 1 ian ian 4 2009-08-14 18:25 f1      

如果為一個并不存在的檔案指定檔案名,那麼

touch

通常會為您建立一個空檔案,除非您指定了

-c

--no-create

選項。清單 15

展示了這兩個指令。注意,隻有f2

被建立。

15. 使用

touch 建立空檔案

[ian@echidna lpi103-2]$ touch f2; touch -c f3; ls -l f*      
-rw-rw-r--. 1 ian ian 4 2009-08-14 18:25 f1      
-rw-rw-r--. 1 ian ian 0 2009-08-14 18:27 f2      

touch

指令還可以将檔案的修改時間(也稱為

mtime)設定為一個特定日期和時間,可以使用

-d

-t

-d

可以非常靈活地處理它将接受的日期和時間格式,而

-t

選項需要至少一個MMDDhhmm

時間,年份和秒數是可選的。清單16

展示了一些例子。

16. 使用

touch 設定

mtime

[ian@echidna lpi103-2]$ touch -t 200908121510.59 f3      
[ian@echidna lpi103-2]$ touch -d 11am f4      
[ian@echidna lpi103-2]$ touch -d "last fortnight" f5      
[ian@echidna lpi103-2]$ touch -d "yesterday 6am" f6      
[ian@echidna lpi103-2]$ touch -d "2 days ago 12:00" f7      
[ian@echidna lpi103-2]$ touch -d "tomorrow 02:00" f8      
[ian@echidna lpi103-2]$ touch -d "5 Nov" f9      
[ian@echidna lpi103-2]$ ls -lrt f*      
-rw-rw-r--. 1 ian ian 0 2009-07-31 18:31 f5      
-rw-rw-r--. 1 ian ian 0 2009-08-12 12:00 f7      
-rw-rw-r--. 1 ian ian 0 2009-08-12 15:10 f3      
-rw-rw-r--. 1 ian ian 0 2009-08-13 06:00 f6      
-rw-rw-r--. 1 ian ian 0 2009-08-14 11:00 f4      
-rw-rw-r--. 1 ian ian 4 2009-08-14 18:25 f1      
-rw-rw-r--. 1 ian ian 0 2009-08-14 18:27 f2      
-rw-rw-r--. 1 ian ian 0 2009-08-15 02:00 f8      
-rw-rw-r--. 1 ian ian 0 2009-11-05 00:00 f9      

如果您不确定某個日期表達式所表示的日期,那麼可以使用

date

指令确定日期。它還接受

-d

選項并解析與

touch

相同的月曆格式。

-r

--reference

)選項以及一個引用檔案名

來表示

touch

date

)應當使用現有檔案的時間戳。清單17

給出了一些示例。

17. 引用檔案的時間戳

[ian@echidna lpi103-2]$ date      
Fri Aug 14 18:33:48 EDT 2009      
[ian@echidna lpi103-2]$ date -r f1      
Fri Aug 14 18:25:50 EDT 2009      
[ian@echidna lpi103-2]$ touch -r f1 f1a      
[ian@echidna lpi103-2]$ ls -l f1*      
-rw-rw-r--. 1 ian ian 4 2009-08-14 18:25 f1      
-rw-rw-r--. 1 ian ian 0 2009-08-14 18:25 f1a      

系統同時記錄檔案修改

時間和檔案通路

時間。這兩個時間也分别被稱為

mtime

atime。當檔案被建立時,這兩個時間戳均被設定為相同的值,在檔案被修改時,兩個值同時被重置。如果檔案被通路過,那麼通路時間将被更新,即使檔案未被修改。對于我們的最後一個

touch

例子,我們将檢視檔案通路

時間。

-a

--time=atime

--time=access

--time=use

)選項表示通路時間應該被更新。清單 18

cat

指令通路 f1

檔案并顯示其内容。我們随後使用

ls -l

ls -lu

分别顯示 f1

和 f1a

的修改和通路時間,f1a

是使用 f1

作為引用檔案建立的。我們随後使用

touch -a

将 f1

的通路時間重置為 f1a

的通路時間,然後檢驗它是否被重置。

18. 通路時間和修改時間

[ian@echidna lpi103-2]$ ls -lu f1*      
-rw-rw-r--. 1 ian ian 4 2009-08-14 18:39 f1      
-rw-rw-r--. 1 ian ian 0 2009-08-14 18:25 f1a      
[ian@echidna lpi103-2]$ ls -l f1*      
-rw-rw-r--. 1 ian ian 4 2009-08-14 18:25 f1      
-rw-rw-r--. 1 ian ian 0 2009-08-14 18:25 f1a      
[ian@echidna lpi103-2]$ touch -a -r f1a f1      
[ian@echidna lpi103-2]$ ls -lu f1*      
-rw-rw-r--. 1 ian ian 4 2009-08-14 18:25 f1      
-rw-rw-r--. 1 ian ian 0 2009-08-14 18:25 f1a      

有關大量可用日期和時間規範的更多完整資訊,請參考手冊或資訊頁中有關

touch

date

指令的内容。

查找檔案

現在我們已經介紹了檔案和目錄主題中的遞歸和 globbing

内容,遞歸涉及檔案的所有方面,而 globbing

更具有針對性,讓我們看看

find

指令,該指令更像是外科醫生的手術刀。

find

指令用于根據名稱、時間戳或大小等條件查找一個或多個目錄樹中的檔案。我們再一次使用lpi103-2

find

find

指令将使用全名或部分名稱搜尋檔案或目錄,或者按照其他搜尋條件搜尋,例如大小、類型、檔案所有者、建立日期或最近一次的通路時間。最基本的查找是按全名或部分名稱查找。清單19

展示了lpi103-2

目錄中的一個例子,我們将查找檔案名中包含'1'

或'k'

的所有檔案,然後執行一些路徑搜尋,下面的說明部分将加以解釋。

19. 按名稱查找檔案

[ian@echidna lpi103-2]$ find . -name "*[1k]*"      
./f1a      
./f1      
./text10      
./backup      
./backup/text1.bkp.1      
./backup/text1.bkp.2      
./text1      
[ian@echidna lpi103-2]$ find . -ipath "*ACK*1"      
./backup/text1.bkp.1      
[ian@echidna lpi103-2]$ find . -ipath "*ACK*/*1"      
[      

說明:

您可以使用 shell

通配符模式,比如前面

通配符和

globbing

小節中的模式。

-path

代替

-name

來比對完全路徑而不是基本檔案名。在本例中,模式可能跨越路徑元件,這與普通通配符比對不同,後者隻比對路徑的某個部分。

如果希望進行不區分大小寫的搜尋,如前面的

ipath

所示,那麼在搜尋字元串或模式的

find

選項前面加一個 'i'。

如果希望搜尋名稱以點開頭的檔案或目錄名,比如 .bashrc

或目前目錄 (.),那麼您必須将一個前導圓點指定為模式的一部分。否則,名稱搜尋将忽略這些檔案或目錄。

在上面的第一個例子中,我們查找所有檔案和一個目錄(./backup)。使用

-type

參數以及一個單字母類型來限制搜尋。使用 'f'

表示普通檔案,使用 'd'

查找目錄,使用 'l'

查找符号連結。參考手冊頁,獲得

find

的其他可能類型。清單 20

展示了隻搜尋目錄的結果(

-type d

)和檔案名(*,在本例中可為任何内容)搜尋結果。

20. 按類型搜尋檔案

[ian@echidna lpi103-2]$ find . -type d      
.      
./backup      
[ian@echidna lpi103-2]$ find . -type d -name "*"      
.      
./backup      

-type d

不包含任何形式的名稱指定,将顯示在其名稱中具有一個前導圓點的目錄(在本例中隻包括目前目錄),和通配符“*”

的作用相同。

我們還可以按照檔案大小進行搜尋,可以搜尋具有指定大小的檔案 (n),或者搜尋檔案大小大于

(+n) 或小于 (-n)

某個給定值的檔案。通過使用上限和下限大小,我們可以查找大小在某個給定範圍内的檔案。預設情況下,

find

-size

選項假設包含 512

位元組的塊為一個機關,用 'b'

表示。此外,指定 'c'

表示位元組,或指定 'k'

表示千位元組。在清單 21

中,我們首先查找大小為 0

的所有檔案,然後查找大小為24

或 25

位元組的所有檔案。注意,指定

-empty

而不是

-size 0

也将查找空檔案。

21. 按大小查找檔案

[ian@echidna lpi103-2]$ find . -size 0      
./f1a      
./f6      
./f8      
./f2      
./f3      
./f7      
./f4      
./f9      
./f5      
[ian@echidna lpi103-2]$ find . -size -26c -size +23c -print      
./text2      
./text5      
./backup/text1.bkp.1      
./backup/text1.bkp.2      
./text1      

清單 21

中的第二個例子引入了

-print

選項,它示範了一個可能會對搜尋傳回的結果執行的操作。在

bash shell 中,如果沒有指定任何操作的話,那麼這将是預設操作。在某些系統和 shell

中,需要指定一個操作;否則,不會生成輸出。

其他操作包括

-ls

(輸出檔案資訊,與

ls -lids

指令的輸出差不多)和

-exec

(為每個檔案執行一個指令)。

-exec

必須以分号終止,必須對分号進行轉義,以避免 shell

首先解釋分号。如果希望在指令中使用傳回的檔案,還必須指定{}。記住,花括号對于shell

也是有意義的,是以必須進行轉義(或引用)。清單22

展示了如何使用

-ls

-exec

選項列出檔案資訊。注意第二個表單沒有列出inode

資訊。

22. 查找和處理檔案

[ian@echidna lpi103-2]$ find . -size -26c -size +23c -ls      
933893    4 -rw-rw-r--   1 ian      ian            25 Aug 11 14:27 ./text2      
933900    4 -rw-rw-r--   1 ian      ian            24 Aug 11 18:47 ./text5      
934193    4 -rw-rw-r--   1 ian      ian            24 Aug 12 15:36 ./backup/text1.bkp.1      
934195    4 -rw-rw-r--   1 ian      ian            24 Aug 12 15:36 ./backup/text1.bkp.2      
933892    4 -rw-rw-r--   1 ian      ian            24 Aug 11 14:02 ./text1      
[ian@echidna lpi103-2]$ find . -size -26c -size +23c -exec ls -l '{}' \;      
-rw-rw-r--. 1 ian ian 25 2009-08-11 14:27 ./text2      
-rw-rw-r--. 1 ian ian 24 2009-08-11 18:47 ./text5      
-rw-rw-r--. 1 ian ian 24 2009-08-12 15:36 ./backup/text1.bkp.1      
-rw-rw-r--. 1 ian ian 24 2009-08-12 15:36 ./backup/text1.bkp.2      
-rw-rw-r--. 1 ian ian 24 2009-08-11 14:02 ./text1      

-exec

選項可用于您能夠想象到的所有用途。例如:

find . -empty -exec rm '{}' \;

删除目錄樹中的所有空檔案

find . -name "*.htm" -exec mv'{}' '{}l' \;

将所有 .htm檔案重命名為.html

檔案。

在最後一個

find

例子中,我們使用由

touch

指令描述的時間戳來查找具有特定時間戳的檔案。清單23

給出了三個例子:

當用于

-mtime -2

時,

find

指令将查找在最近兩天以内修改的所有檔案。在本例中,一天即指與目前日期和時間相關的 24

個小時。注意,如果您希望根據通路時間而不是修改時間查找檔案,那麼将使用

-atime

添加

-daystart

選項意味着我們希望使用月曆計算天數,從午夜開始。現在,f3

已被排除在清單以外。

最後,我們将展示如何使用以分鐘計算的時間範圍而不是天數來查找在過去的 1

個小時(60

分鐘)至 10 個小時(600

分鐘)之間發生修改的檔案。

23. 按時間戳查找檔案

[ian@echidna lpi103-2]$ date      
Sat Aug 15 00:27:36 EDT 2009      
[ian@echidna lpi103-2]$ find . -mtime -2 -type f -exec ls -l '{}' \;      
-rw-rw-r--. 1 ian ian 0 2009-08-14 18:25 ./f1a      
-rw-rw-r--. 1 ian ian 4 2009-08-14 18:25 ./f1      
-rw-rw-r--. 1 ian ian 0 2009-08-13 06:00 ./f6      
-rw-rw-r--. 1 ian ian 0 2009-08-15 02:00 ./f8      
-rw-rw-r--. 1 ian ian 0 2009-08-14 18:27 ./f2      
-rw-rw-r--. 1 ian ian 58 2009-08-14 17:30 ./text10      
-rw-rw-r--. 1 ian ian 0 2009-08-14 11:00 ./f4      
-rw-rw-r--. 1 ian ian 0 2009-11-05 00:00 ./f9      
[ian@echidna lpi103-2]$ find . -daystart -mtime -2 -type f -exec ls -l '{}' \;      
-rw-rw-r--. 1 ian ian 0 2009-08-14 18:25 ./f1a      
-rw-rw-r--. 1 ian ian 4 2009-08-14 18:25 ./f1      
-rw-rw-r--. 1 ian ian 0 2009-08-15 02:00 ./f8      
-rw-rw-r--. 1 ian ian 0 2009-08-14 18:27 ./f2      
-rw-rw-r--. 1 ian ian 58 2009-08-14 17:30 ./text10      
-rw-rw-r--. 1 ian ian 0 2009-08-14 11:00 ./f4      
-rw-rw-r--. 1 ian ian 0 2009-11-05 00:00 ./f9      
[ian@echidna lpi103-2]$ find . -mmin -600 -mmin +60 -type f -exec ls -l '{}' \;      
-rw-rw-r--. 1 ian ian 0 2009-08-14 18:25 ./f1a      
-rw-rw-r--. 1 ian ian 4 2009-08-14 18:25 ./f1      
-rw-rw-r--. 1 ian ian 0 2009-08-14 18:27 ./f2      
-rw-rw-r--. 1 ian ian 58 2009-08-14 17:30 ./text10      

find

指令的手冊頁可以幫助您了解各種選項,我們無法在這篇簡短的介紹中一一介紹它們。

識别檔案

檔案名通常具有一個字尾,比如 gif、jpeg

或 html,它将告訴您檔案中可能包含的内容。Linux

不需要這種字尾,是以通常不會使用它們來識别檔案類型。了解正在處理的檔案的類型将使您知道程式将使用什麼工具顯示或操作檔案。

file

指令将使您了解到一個或多個檔案中的資料的類型。清單 24

展示了使用

file

指令的一些例子。

24. 識别檔案内容

[ian@echidna lpi103-2]$ file backup text1 f2 ../p-ishields.jpg /bin/echo      
backup:            directory      
text1:             ASCII text      
f2:                empty      
../p-ishields.jpg: JPEG image data, JFIF standard 1.02      
/bin/echo:         ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically       
linked (uses shared libs), for GNU/Linux 2.6.18, stripped      

file

指令嘗試使用三種測試來對每個檔案進行分類。比如,檔案系統測試使用

stat

指令的結果來判斷某個檔案或目錄是否為空。名為

magic

的測試将檢查檔案中的特定内容,這些内容可以識别檔案。這些簽名也被稱為魔術數字(magicnumber)。最後,語言測試将檢視文本檔案中的内容,嘗試确定某個檔案是

XML 檔案、C

或 C++

語言源檔案、troff

檔案,或是其他被認為是某些語言處理器的源檔案。找到的第一種類型将被生成報告,除非

-k

--keep-going

選項被指定。

file

指令具有許多選項,您可以通過手冊頁獲得詳細介紹。清單25

-i

--mime

)選項來将檔案類型顯示為MIME

字元串,而不是普通的人類可讀輸出。

25. 将檔案内容識别為

MIME

[ian@echidna lpi103-2]$ file -i backup text1 f2 ../p-ishields.jpg /bin/echo      
backup:            application/x-directory; charset=binary      
text1:             text/plain; charset=us-ascii      
f2:                application/x-empty; charset=binary      
../p-ishields.jpg: image/jpeg; charset=binary      
/bin/echo:         application/x-executable; charset=binary      

魔術數字檔案也由

file

指令管理。請在此檢視手冊頁獲得更多資訊。

注意:作為

ImageMagick 包的一部分,

identify

指令可以用作額外一種工具,為識别圖像檔案類型提供更多細節。

壓縮檔案

當您備份、歸檔或傳輸檔案時,通常會對檔案進行壓縮。在 Linux

環境中,兩個流行的壓縮程式為

gzip

bzip2

gzip

指令使用Lempel-Ziv

算法,而

bzip2

使用Burrows-Wheeler

塊分類算法。

使用 gzip

和 gunzip

壓縮通常對文本檔案比較有效。許多圖像格式的檔案已經進行了資料壓縮,是以對這些或其他二進制檔案進行壓縮效果不大。為了示範對較大文本檔案執行的壓縮,讓我們将/etc/services

複制到我們一直使用的目錄中,并使用gzip

對它執行壓縮,如清單26

所示。我們使用

cp

-p

選項保留/etc/services

的時間戳。注意,壓縮後的檔案具有相同的時間戳,并且具有一個.gz

字尾。

26. 使用

gzip 進行壓縮

[ian@echidna lpi103-2]$ cp -p /etc/services      
[ian@echidna lpi103-2]$ ls -l serv*      
-rw-r--r--. 1 ian ian 630983 2009-04-10 04:42 services      
[ian@echidna lpi103-2]$ gzip services      
[ian@echidna lpi103-2]$ ls -l serv*      
-rw-r--r--. 1 ian ian 124460 2009-04-10 04:42 services.gz      

您使用

gzip

-d

選項解壓縮使用 gzip

壓縮過的檔案,更常見的是使用

gunzip

指令。清單 27

展示了第一種方法。注意,未壓縮的檔案現在具有原始名和時間戳。

27. 使用

gzip 解壓縮

[ian@echidna lpi103-2]$ gzip -d services.gz      
[ian@echidna lpi103-2]$ ls -l serv*      
-rw-r--r--. 1 ian ian 630983 2009-04-10 04:42 services      
使用 bzip2

和 bunzip2

bzip2

指令以與

gzip

類似的方式運作,如清單 28所示。

28. 使用

bzip2 進行壓縮

[ian@echidna lpi103-2]$ ls -l serv*      
-rw-r--r--. 1 ian ian 630983 2009-04-10 04:42 services      
[ian@echidna lpi103-2]$ bzip2 services      
[ian@echidna lpi103-2]$ ls -l serv*      
-rw-r--r--. 1 ian ian 113444 2009-04-10 04:42 services.bz2      
[ian@echidna lpi103-2]$ bunzip2 services.bz2      
[ian@echidna lpi103-2]$ ls -l serv*      
-rw-r--r--. 1 ian ian 630983 2009-04-10 04:42 services      
gzip

和 bzip2

之間的差別

根據設計,

bzip2

的許多選項與

gzip

的選項是相似的,但是這兩個指令不包含完全相同的選項。您可能已經注意到,在我們的兩個例子中,未壓縮的檔案具有與原始檔案相同的檔案名和時間戳。然而,重命名或touch

已壓縮檔案會改變這一行為。

gzip

-N

--name

選項會強制保留名稱和時間戳,但是

bzip2

不會。

gzip

指令還有一個

-l

選項,用于顯示與已壓縮檔案有關的資訊,包括将在解壓縮時使用的名稱。清單 29

解釋了這兩個指令之間的一些差別。

29. gzip 和

bzip2 之間的差別

[ian@echidna ~]$ ls -l serv*      
-rw-r--r--. 1 ian ian 630983 2009-04-10 04:42 services      
[ian@echidna ~]$ gzip -N services      
[ian@echidna ~]$ touch services.gz      
[ian@echidna ~]$ mv services.gz services-x.gz      
[ian@echidna ~]$ ls -l serv*      
-rw-r--r--. 1 ian ian 124460 2009-09-23 14:08 services-x.gz      
[ian@echidna ~]$ gzip -l services-x.gz      
         compressed        uncompressed  ratio uncompressed_name      
             124460              630983  80.3% services-x      
[ian@echidna ~]$ gzip -lN services-x.gz      
         compressed        uncompressed  ratio uncompressed_name      
             124460              630983  80.3% services      
[ian@echidna ~]$ gunzip -N services-x.gz      
[ian@echidna ~]$ ls -l serv*      
-rw-r--r--. 1 ian ian 630983 2009-04-10 04:42 services      
[ian@echidna ~]$      
[ian@echidna ~]$ bzip2 services      
[ian@echidna ~]$ mv services.bz2 services-x.bz2      
[ian@echidna ~]$ touch services-x.bz2      
[ian@echidna ~]$ ls -l serv*      
-rw-r--r--. 1 ian ian 113444 2009-09-23 14:10 services-x.bz2      
[ian@echidna ~]$ bunzip2 services-x.bz2      
[ian@echidna ~]$ ls -l serv*      
-rw-rw-r--. 1 ian ian 630983 2009-09-23 14:10 services-x      
[ian@echidna ~]$ rm services-x # Don't need this any more      

gzip

bzip2

都接受來自 stdin

的輸入。兩者都支援用

-c

選項将輸出導出到stdout。

bzip2

還有另外兩個相關的指令。

bzcat

指令将檔案解壓縮到 stdout,其作用等同于

bzip2 -dc

bzip2recover

指令嘗試從受損害的 bzip2 檔案中恢複資料。

手冊頁将幫助您了解

gzip

bzip2

的其他選項。

其他壓縮工具

另外兩個更舊一點的程式是

compress

uncompress

,它們仍然頻繁出現在 Linux

和 UNIX

系統中。

此外,來自 Info-ZIP

項目的

zip

unzip

指令也在 Linux

中得到了實作。它們提供了跨平台的壓縮功能,可以廣泛應用于各種硬體和作業系統。注意,并不是所有作業系統都支援相同的檔案屬性或檔案系統功能。如果您下載下傳用zip

壓縮過的産品檔案并在Windows

系統上解壓縮它,然後将結果檔案傳輸到CD

或 DVD中以用于Linux

安裝,您可能會遇到安裝問題,原因在于,例如,Windows系統不支援原始的未壓縮檔案集所含的符号連結。

有關這些或其他壓縮程式的更多資訊,請參考相關的手冊頁。

歸檔檔案

tar

cpio

dd

指令被常用于備份檔案組甚至是整個分區,目的是歸檔檔案或傳輸到另外一個使用者或站點。LPIC-2認證中的考試201

關注更加詳細的備份事項。

有三種常用的備份方法:

差異

或累積

備份是指對自上一次完全備份之後發生更改的所有内容進行備份。要執行恢複,需要最近一次的完全備份和最新的差異備份。

增量

備份是指隻對自上一次增量備份後發生變化的内容執行備份。要執行恢複,需要最近一次的完全備份以及這次完全備份之後的所有增量備份(按順序)。

完全

備份是指一次完整的備份,通常為整個檔案系統、目錄或相關檔案組。這種備份所需的時間是三種備份中最長的,是以通常與其他兩種方法的其中一種結合使用。

這些指令以及本文中學習的其他指令為您提供了執行任何這些備份任務的工具。

使用 tar

tar

(最初來自

Tape ARchive)從一組輸入檔案或目錄中建立一個歸檔檔案,即

tarfile或

tarball;它還從此類歸檔中恢複檔案。如果某個目錄被作為輸入提供給

tar

,所有檔案和子目錄都被自動包括,這使得

tar

可以非常友善地歸檔您的目錄結構的子樹。

輸出可以被導入到檔案、錄音帶或軟碟之類的裝置或是 stdout

中。輸出位置使用

-f

選項指定。其他常見選項為:

-c

用于建立歸檔,

-x

用于提取歸檔,

-v

用于詳細輸出,它将列出被處理的檔案,

-z

用于使用 gzip

壓縮,而

-j

用于使用 bzip2

壓縮。大部分

tar

選項包含使用一個連字元的簡單格式和使用一對連字元的詳細格式。這裡解釋了簡單格式。參考手冊頁,獲得有關詳細格式和額外選項的内容。

清單 30

tar

建立lpi103-2

目錄的備份。

30. 使用

tar 備份

lpi103-2 目錄

[ian@echidna lpi103-2]$ tar -cvf ../lpitar1.tar .      
./      
./text3      
./yab      
...      
./f5      

您通常希望壓縮歸檔檔案來節省空間或減少傳輸時間。

tar

GNU 版本允許您使用一個單個選項實作此目的—

-z

表示使用

gzip

-b

bzip2

壓縮。清單 31

-z

選項的使用,以及兩種歸檔檔案在大小方面的差異。

31. 使用

gzip 壓縮

tar 歸檔

[ian@echidna lpi103-2]$ tar -zcvf ../lpitar2.tar ~/lpi103-2/      
tar: Removing leading `/' from member names      
/home/ian/lpi103-2/      
/home/ian/lpi103-2/text3      
/home/ian/lpi103-2/yab      
...      
/home/ian/lpi103-2/f5      
[ian@echidna lpi103-2]$ ls -l ../lpitar*      
-rw-rw-r--. 1 ian ian 30720 2009-09-24 15:38 ../lpitar1.tar      
-rw-rw-r--. 1 ian ian   881 2009-09-24 15:39 ../lpitar2.tar      

清單 31

還顯示了

tar

的另外一個重要特性。我們使用了一個絕對目錄路徑,而輸出的第一行告訴我們

tar

正在從成員名稱中删除前導斜線(/)。這允許将檔案恢複到其他位置以進行檢驗,如果您嘗試恢複系統檔案的話,這一點尤其重要。如果您确實需要存儲絕對名稱,那麼使用

-p

選項。在建立歸檔時,避免将絕對路徑名和相對路徑名混在一起始終是一種好的想法,因為在從歸檔恢複時,所有名稱都将為相對名稱。

tar

指令可以使用

-r

--append

選項将額外的檔案附加到歸檔中。這會造成歸檔中出現檔案的多個副本。在這種情況下,最後

一個檔案将在恢複操作期間被恢複。您可以使用

--occurrence

選項從多個檔案中選擇一個特定檔案。如果歸檔位于正常檔案系統而非錄音帶中,那麼可以使用

-u

--update

選項來更新一個歸檔。這種工作方式類似于附加到一個歸檔,差別在于,歸檔中的檔案的時間戳将與檔案系統中的時間戳進行比較,并且隻有在歸檔版本之後發生修改的檔案被附加。如前所述,這不适用于錄音帶歸檔。

tar

指令也可以将歸檔與目前檔案系統加以比較,并恢複來自歸檔的檔案。使用

-d

--compare

--diff

選項執行比較。輸出将顯示内容有差異的檔案,以及時間戳不同的檔案。一般情況下,将隻列出不同的檔案(如果有的話)。使用前面讨論的

-v

選項獲得詳細輸出。

-C

-- directory

選項将告訴

tar

從指定的目錄開始執行操作,而不是從目前目錄開始。

清單 32

展示了一些例子。我們使用

touch

來修改 f1

檔案的時間戳,然後示範了

tar

的比較操作,之後從其中一個歸檔中恢複 f1。我們将使用各種操作來進行說明。

32. 使用 tar

執行比較和恢複

[ian@echidna lpi103-2]$ touch f1      
[ian@echidna lpi103-2]$ tar --diff --file ../lpitar1.tar .      
./f1: Mod time differs      
[ian@echidna lpi103-2]$ tar -df ../lpitar2.tar -C /      
home/ian/lpi103-2/f1: Mod time differs      
[ian@echidna lpi103-2]$ tar -xvf ../lpitar1.tar ./f1 # See below      
./f1      
[ian@echidna lpi103-2]$ tar --compare -f ../lpitar2.tar --directory /      

您為恢複指定的檔案或目錄必須比對歸檔中的名稱。在本例中,如果嘗試隻恢複 f1

而不是 ./f1,那麼将是無效的。您可以使用

globbing,但是需要注意不要恢複過多或過少的内容。如果您不确定歸檔内容的話,您可以使用

--list

-t

選項來列出歸檔内容。清單33

展示了一個通配符說明示例,它将恢複更多的檔案,而不僅僅是./f1。

33. 使用

tar 列出歸檔内容

[ian@echidna lpi103-2]$ tar -tf ../lpitar1.tar "*f1*"      
./f1a      
./f1      

find

指令選擇要歸檔的檔案,然後将結果傳遞給tar。我們将在讨論

cpio

時探讨這種方法,但是同樣的方法也可以用于

tar

和本文介紹的其他指令一樣,這篇簡要的介紹無法涵蓋所有的選項。請參考手冊頁或資訊頁了解更多内容。

使用 cpio

cpio

指令在

copy-out

模式下建立歸檔,在

copy-in模式下恢複歸檔,或在

copy-pass

模式下将一組檔案從一個位置複制到另一個位置。您将對 copy-out

模式使用

-o

--create

選項,對 copy-in

-i

--extract

選項,而對copy-pass

-p

--pass-through

選項。輸入是在stdin

中提供的一組檔案。輸出被指向stdout,或者是由

-f

--file

選項指定的裝置或檔案。

清單 34

find

指令生成一組檔案,并将清單傳遞給

cpio

。注意,對

find

使用了

-print0

選項來為檔案名生成null-terminate

字元串,而

cpio

上的對應的

--null

選項将讀取這個格式。這将正确地處理包含内嵌空格或換行符的檔案名。

-depth

選項告訴

find

在目錄名的前面列出目錄條目。在本例中,我們僅僅建立了 lpi103-2

目錄的兩個歸檔,一個使用相對名稱,一個使用絕對名稱。我們沒有使用

find

的衆多功能對所選檔案進行限制,比如隻查找在本周修改過的檔案。

34. 使用

cpio 備份目錄

[ian@echidna lpi103-2]$ find . -depth -print0 | cpio --null -o > ../lpicpio.1      
3 blocks      
[ian@echidna lpi103-2]$ find ~/lpi103-2/ -depth -print0 | cpio --null -o > ../lpicpio.2      
4 blocks      

如果您希望按照歸檔順序列出檔案,那麼對

cpio

-v

copy-in

模式下的

cpio

指令(選項

-i

--extract

)可以列出歸檔的内容或恢複所選檔案。當您列出檔案時,指定

--absolute-filenames

選項将減少無關消息的數量,較舊版本的

cpio

在從包含前導 /

字元的每個路徑中分離該字元時會發出這些消息。該選項在許多最新實作中都被忽略。選擇性列出前面的歸檔的輸出如清單35

35. 使用

cpio 列出和恢複所選檔案

[ian@echidna lpi103-2]$ cpio  -i --list  "*backup*" < ../lpicpio.1      
backup      
backup/text1.bkp.1      
backup/text1.bkp.2      
3 blocks      
[ian@echidna lpi103-2]$ cpio  -i --list absolute-filenames "*text1*" < ../lpicpio.2      
/home/ian/lpi103-2/text10      
/home/ian/lpi103-2/backup/text1.bkp.1      
/home/ian/lpi103-2/backup/text1.bkp.2      
/home/ian/lpi103-2/text1      
4 blocks      

清單 36

展示了如何将路徑中含有 “text1”

的所有檔案恢複到一個臨時子目錄中。其中一些檔案位于子目錄中。與

tar

不同,如果目錄樹不存在的話,您将需要明确地指定

-d

--make-directories

選項。此外,

cpio

不會使用歸檔副本替換檔案系統中任何較新的檔案,除非您指定了

-u

--unconditional

36. 使用

cpio 恢複所選檔案

[ian@echidna lpi103-2]$ mkdir temp      
[ian@echidna lpi103-2]$ cd temp      
[ian@echidna temp]$ cpio  -idv "*f1*" "*.bkp.1" < ../../lpicpio.1      
f1a      
f1      
backup/text1.bkp.1      
3 blocks      
[ian@echidna temp]$ cpio  -idv "*.bkp.1" < ../../lpicpio.1      
cpio: backup/text1.bkp.1 not created: newer or same age version exists      
backup/text1.bkp.1      
3 blocks      
[ian@echidna temp]$ cpio  -id --no-absolute-filenames "*text1*" < ../../lpicpio.2      
cpio: Removing leading `/' from member names      
4 blocks      
./home/ian/lpi103-2/backup/text1.bkp.1      
./home/ian/lpi103-2/backup/text1.bkp.2      
./home/ian/lpi103-2/text1      
./backup/text1.bkp.1      
[ian@echidna temp]$ cd ..      
[ian@echidna lpi103-2]$ rm -rf temp # You may remove these after you have finished      

有關其他選項的更多細節,請參考手冊頁。

就其最簡單形式而言,

dd

指令将一個輸入檔案複制到一個輸出檔案。您已經了解過

cp

指令,是以您可能希望知道為什麼還要用另外一個指令複制檔案。

dd

指令可以完成很多正常

cp

指令無法辦到的事情。特别是,它可以對檔案執行轉換,比如将小寫轉換為大寫,或将 ASCII轉換為EBCDIC。它還可以重新阻塞(reblock)一個檔案,當将檔案傳輸給錄音帶時可能需要這樣做。它還可以跳過或隻包括所選的檔案塊。最後,它可以讀取和寫入原始裝置,比如/dev/sda,這允許您建立和恢複作為完整分區映像的檔案。寫入到裝置通常需要根權限。

我們将首先來看一個使用

conv

将檔案轉換為大寫的簡單例子,如清單 37

if

選項指定輸入檔案,而不是使用預設的 stdin。類似的

of

選項可用于覆寫 stdout

預設輸出。為了示範的目的,我們使用

ibs

obs

選項指定了不同的輸入和輸出塊大小。對于較大的檔案,在進行磁盤之間的傳輸時,使用較大的塊大小加速操作會比較友善。另外,塊大小最常用于錄音帶。注意,清單末尾的三行狀态代碼表示有多少完整的和局部的塊被讀取和寫入,以及被傳輸的資料的總量。

37. 使用

dd 将文本轉換為大寫

[ian@echidna lpi103-2]$ cat text6      
1 apple      
2 pear      
3 banana      
9       plum      
3       banana      
10      apple      
1 apple      
2 pear      
3 banana      
9       plum      
3       banana      
10      apple      
[ian@echidna lpi103-2]$ dd if=text6 conv=ucase ibs=20 obs=30      
1 APPLE      
2 PEAR      
3 BANANA      
9       PLUM      
3       BANANA      
10      APPLE      
1 APPLE      
2 PEAR      
3 BANANA      
9       PLUM      
3       BANANA      
10      APPLE      
4+1 records in      
3+1 records out      
98 bytes (98 B) copied, 0.00210768 s, 46.5 kB/s      

任何檔案都可能是一個原始裝置。這種情況通常出現在錄音帶中,但是一個完整的磁盤分區,比如 /dev/hda1

或 /dev/sda2,可以被備份到一個檔案或錄音帶中。理想情況下,裝置上的檔案系統應當被解除挂載,或至少為隻讀挂載,進而確定資料在備份期間不會發生修改。清單39

展示了一個示例,其中輸入檔案是一個原始裝置dev/sda3,而輸出檔案是位于根使用者的主目錄中的檔案backup-1。要将檔案轉儲到錄音帶或軟碟中,将進行

of=/dev/fd0

of=/dev/st0

等指定。

38. 使用

dd 備份分區

[root@echidna ~]# dd if=/dev/sda2 of=backup-1      
1558305+0 records in      
1558305+0 records out      
797852160 bytes (798 MB) copied, 24.471 s, 32.6 MB/s      

注意,797,852,160

位元組的資料被複制,而輸出檔案也包含這麼多的資料,即使實際上隻會使用 3%

的分區。除非您使用硬體壓縮将檔案複制到磁盤,否則您将希望對資料進行壓縮。清單39

展示了實作此目的的一種方法,以及

ls

df

指令的輸出,向您展示了檔案的大小和/dev/sda3

上的檔案系統的使用百分比。

39. 使用

dd 實作壓縮備份

[root@echidna ~]# dd if=/dev/sda2 |gzip >backup-2      
1558305+0 records in      
1558305+0 records out      
797852160 bytes (798 MB) copied, 23.4617 s, 34.0 MB/s      
[root@echidna ~]# ls -l backup-[12]      
-rw-r--r--. 1 root root 797852160 2009-09-25 17:13 backup-1      
-rw-r--r--. 1 root root    995223 2009-09-25 17:14 backup-2      
[root@echidna ~]# df -h /dev/sda2      
Filesystem            Size  Used Avail Use% Mounted on      
/dev/sda2             755M   18M  700M   3% /grubfile      

壓縮将檔案大小減小到未壓縮大小的 20%。然而,未使用的塊可能包含任何資料,是以即使壓縮過的備份也可能要比分區的總資料大。

如果按照處理的記錄的數量對總位元組進行劃分,您将看到

dd

将寫入多個包含 512

位元組的資料塊。當複制到錄音帶之類的原始輸出裝置中時,這将導緻産生非常低效的操作。如前所述,指定

obs

選項來改變輸出的大小,或用

ibs

選項來指定輸入塊大小。您還可以僅指定

bs

來将輸入和輸出塊大小同時指定為一個常用值。在使用錄音帶時,記住使用相同的塊大小來讀取和寫入錄音帶。

如果需要多個錄音帶或其他可移動存儲來儲存備份,那麼需要使用

split

之類的工具将它們分為更小的部分。如果需要跳過磁盤或錄音帶标簽之類的塊,可以使用

dd

完成。參見手冊頁中的示例。

dd

指令無法被檔案系統感覺,是以需要恢複一個分區的轉儲才能知道分區的内容。清單40

展示了如何将清單39

中轉儲的檔案恢複到分區/dev/sdc,該分區建立在可移動USB

驅動上,專門用于這個用途。

40. 使用

dd 恢複分區

[root@echidna ~]# gunzip backup-2 -c | dd  of=/dev/sdc7      
1558305+0 records in      
1558305+0 records out      
797852160 bytes (798 MB) copied, 30.624 s, 26.1 MB/s      

您可能對一些使用

dd

指令在幕後執行實際的裝置寫入工作的CD

和 DVD刻錄應用程式感興趣。如果您使用的工具提供了大量實際使用的指令,并且您現在對

dd

有了更多的了解,那麼您會發現檢視日志會大有裨益。實際上,如果您将一個ISO

映像刻錄到一張CD

或 DVD磁盤,一種确定沒有錯誤的方法就是使用

dd

回讀磁盤并通過

cmp

工具傳遞結果。清單 41

使用我們在本文中建立的備份檔案解釋了這一常見技巧,而不是使用ISO

映像。注意,我們使用映像的檔案大小計算讀取的塊的數量。

41. 比較映像和檔案系統

[root@echidna ~]# ls -l backup-1      
-rw-r--r--. 1 root root 797852160 2009-09-25 17:13 backup-1      
[root@echidna ~]# echo $(( 797852160 / 512 )) # calculate number of 512 byte blocks      
1558305      
[root@echidna ~]# dd if=/dev/sdc7 bs=512 count=1558305 | cmp - backup-1      
1558305+0 records in      
1558305+0 records out      
797852160 bytes (798 MB) copied, 26.7942 s, 29.8 MB/s      

流、管道和重定向

概述

本文幫助您鞏固重定向标準 I/O

流的基礎 Linux

技術。您将學習如何:

重定向标準 I/O

流:标準輸出和标準錯誤

通過管道将一個指令的輸出導入到另一個指令的輸入

将輸出發送到 stdout

和檔案中

将指令輸出用作另一個指令的參數

本文幫助您準備 Linux Professional Institute's Junior Level Administration(LPIC-1)

考試101

的主題103

下的考核目标103.4。該考核目标的權值為4。

設定示例

在本文中,我們将使用在文章 “

中建立的一些檔案練習指令。即使您沒有閱讀那篇文章或者沒有儲存所建立的檔案,也一樣能順利學習本文。我們首先在您的主目錄下建立一個名為lpi103-4

的子目錄并在其中建立必要的檔案。為此,在作為目前目錄的主目錄下打開一個文本視窗,将清單1

中的内容複制到文本視窗并運作指令。完成之後就建立了您将要使用的lpi103-4

子目錄和檔案。

1. 建立示例檔案

mkdir -p lpi103-4 && cd lpi103-4 && {      
echo -e "1 apple\n2 pear\n3 banana" > text1      
echo -e "9\tplum\n3\tbanana\n10\tapple" > text2      
echo "This is a sentence. " !#:* !#:1->text3      
split -l 2 text1      
split -b 17 text2 y; }      

您的視窗應該類似于清單 2,并且目前的目錄為新建立的

lpi103-4 目錄。

2. 建立示例檔案

- 輸出

[ian@echidna ~]$ mkdir -p lpi103-4 && cd lpi103-4 && {      
> echo -e "1 apple\n2 pear\n3 banana" > text1      
> echo -e "9\tplum\n3\tbanana\n10\tapple" > text2      
> echo "This is a sentence. " !#:* !#:1->text3      
                               echo "This is a sentence. "       
"This is a sentence. " "This is a sentence. ">text3      
> split -l 2 text1      
> split -b 17 text2 y; }      
[ian@echidna lpi103-4]$      
重定向标準 I/O

Linux shell(比如 Bash)接收或發送序列和字元串流

形式的輸入或輸出。每個字元都獨立于與之相鄰的字元。字元沒有被組織成結構化記錄或固定大小的塊。不管實際的字元串流進入或來自檔案、鍵盤、顯示視窗或其他I/O

裝置,都使用檔案I/O

技術來通路流。Linuxshell

使用3

種标準的I/O

流,每種流都與一個檔案描述符相關聯:

stdout

是标準輸出流,它顯示來自指令的輸出。它的檔案描述符為 1。

stderr

是标準錯誤流,它顯示來自指令的錯誤輸出。它的檔案描述符為 2。

stdin

是标準輸入流,它為指令提供輸入。它的檔案描述符為 0。

輸入流通常通過終端擊鍵為程式提供輸入。輸出流通常向終端輸出文本字元。最初的終端是 ASCII

打字機或顯示終端,但現在更多是指圖形桌面上的文本視窗。

如果您已經學習了文章 “

”,那麼就熟悉本文的部分内容。

重定向輸出

可以通過兩種方法将輸出重定向到檔案:

n>

将輸出從檔案描述符

重定向到檔案。您必須具有該檔案的寫權限。如果該檔案不存在,将建立它。如果該檔案已經存在,通常将覆寫所有現有内容,并且沒有任何警告。

n>>

還可以将輸出從檔案描述符

重定向到一個檔案中。這裡也一樣要求您具有該檔案的寫權限。如果該檔案不存在,将建立它。如果該檔案已經存在,輸出将附加到現有的内容後面。

在 n>

或 n>>

中的

引用檔案描述符。如果省略它,将執行标準輸出。清單

3 在我們先前在lpi103-4

目錄中建立的檔案中使用重定向将标準輸出和标準錯誤從

ls

指令分離出來。我們還顯示将輸出附加到現有檔案中。

3. 輸出重定向

[ian@echidna lpi103-4]$ ls x* z*      
ls: cannot access z*: No such file or directory      
xaa  xab      
[ian@echidna lpi103-4]$ ls x* z* >stdout.txt 2>stderr.txt      
[ian@echidna lpi103-4]$ ls w* y*      
ls: cannot access w*: No such file or directory      
yaa  yab      
[ian@echidna lpi103-4]$ ls w* y* >>stdout.txt 2>>stderr.txt      
[ian@echidna lpi103-4]$ cat stdout.txt      
xaa      
xab      
yaa      
yab      
[ian@echidna lpi103-4]$ cat stderr.txt      
ls: cannot access z*: No such file or directory      
ls: cannot access w*: No such file or directory      

使用 n>

的輸出重定向通常覆寫現有的檔案。您可以使用

set

内置控件的

noclobber

選項對此進行控制。如果該選項已經設定,您可以使用n>|

覆寫它,如清單4

4. 帶有

noclobber 選項的輸出重定向

[ian@echidna lpi103-4]$ set -o noclobber      
[ian@echidna lpi103-4]$ ls x* z* >stdout.txt 2>stderr.txt      
-bash: stdout.txt: cannot overwrite existing file      
[ian@echidna lpi103-4]$ ls x* z* >|stdout.txt 2>|stderr.txt      
[ian@echidna lpi103-4]$ cat stdout.txt      
xaa      
xab      
[ian@echidna lpi103-4]$ cat stderr.txt      
ls: cannot access z*: No such file or directory      
[ian@echidna lpi103-4]$ set +o noclobber #restore original noclobber setting      

在某些情況下,您可能想要将标準輸出和标準錯誤都重定向到一個檔案中。這通常為自動程序或背景作業而執行的,以便以後可以檢視輸出。使用&>

或&>>

同時将标準輸出和标準錯誤重定向到同一個檔案中。另一種方法是,首先重定向檔案描述符

n,然後使用 m>&n

或 m>>&n

将檔案描述符

m

重定向到同一個檔案。例如,

command 2>&1 >output.txt

不同于

command >output.txt 2>&1

在第一種情況中,stderr被重定向到stdout

的目前位置,然後在将stdout

重定向到output.txt,但第二次重定向僅影響stdout,不影響stderr。在第二種情況中,stderr被重定向到stdout

的目前位置,即output.txt。我們在清單5

中顯示了這些重定向。注意,在最後一個指令中先重定向标準錯誤在重定向标準輸出,是以标準錯誤輸出仍然列印在終端視窗中。

5. 将兩個流重定向到一個檔案中

[ian@echidna lpi103-4]$ ls x* z* &>output.txt      
[ian@echidna lpi103-4]$ cat output.txt      
ls: cannot access z*: No such file or directory      
xaa      
xab      
[ian@echidna lpi103-4]$ ls x* z* >output.txt 2>&1      
[ian@echidna lpi103-4]$ cat output.txt      
ls: cannot access z*: No such file or directory      
xaa      
xab      
[ian@echidna lpi103-4]$ ls x* z* 2>&1 >output.txt # stderr does not go to output.txt      
ls: cannot access z*: No such file or directory      
[ian@echidna lpi103-4]$ cat output.txt      
xaa      
xab      

不過,有時候您可能想要完全忽略标準輸出或标準錯誤。為此,将選擇的流重定向到空檔案 /dev/null。清單

6 顯示了如何從

ls

指令忽略錯誤輸出,同時也使用

cat

指令顯示 /dev/null

是空的。

6. 使用

/dev/null 忽略輸出

[ian@echidna lpi103-4]$ ls x* z* 2>/dev/null      
xaa  xab      
[ian@echidna lpi103-4]$ cat /dev/null      
重定向輸入

就像可以重定向 stdout

和 stderr

流一樣,我們也可以使用 <

操作符從檔案重定向 stdin。如果您已經學習了文章

”,那麼您可能還會記得我們在

sort 和 uniq

小節中使用

tr

指令将 text1

檔案中的空格替換成制表符。在那個例子中我們使用來自

cat

指令的輸出為

tr

指令建立标準輸入。現在,我們沒有必要調用

cat

,而是使用輸入重定向将空格轉換成制表符,如清單7

7. 輸入重定向

[ian@echidna lpi103-4]$ tr ' ' '\t'<text1      
1       apple      
2       pear      
3       banana      

Shell(包括 bash)還有存在

here-document的概念,它是另一種輸入重定向形式。它将<<

和一個單詞(比如END)結合構成一個标記,用來表示輸入端。我們在清單8

中對此進行示範。

8. 使用

here-document 的輸入重定向

[ian@echidna lpi103-4]$ sort -k2 <<END      
> 1 apple      
> 2 pear      
> 3 banana      
> END      
1 apple      
3 banana      
2 pear      

您可能很想知道可不可以僅輸入

sort -k2

和資料,然後按

Ctrl-d表明輸入端。最簡單的答案是,您可以這樣做,但您必須先了解here-documents。詳細的答案是,here-documents通常用于shell

腳本(腳本沒有其他方式能夠表明應該将腳本的哪一行看作輸入)。因為shell

腳本通過廣泛使用制表符來提供縮進,是以here-documents

還有另一個特點。如果您使用 <<-

而不是 <<,那麼将消除前面的制表符。

在清單 9

中,我們使用指令行替換建立了一個強制制表符,然後建立了一個包含兩個

cat

指令的 shell

腳本,這兩個指令都從 here-document

讀取資料。注意,我們使用 END

作為從終端讀取的here-document

的标記。如果我們在該腳本中也使用END

作為标記,将導緻提前結束輸入。是以我們使用EOF

作為标記。在建立好腳本之後,我們使用點号

.

指令導入它,即在目前的shell

上下文中運作它。

9. 使用

[ian@echidna lpi103-4]$ ht=$(echo -en "\t")      
[ian@echidna lpi103-4]$ cat<<END>ex-here.sh      
> cat <<-EOF      
> apple      
> EOF      
> ${ht}cat <<-EOF      
> ${ht}pear      
> ${ht}EOF      
> END      
[ian@echidna lpi103-4]$ cat ex-here.sh      
cat <<-EOF      
apple      
EOF      
        cat <<-EOF      
        pear      
        EOF      
[ian@echidna lpi103-4]$ . ex-here.sh      
apple      
pear      

在本系列的後續文章中,我們将更詳細地介紹指令替換和腳本。檢視我們的

獲得本系列所有文章的簡介和連結。

建立管道

在文章

中,我們這樣描述文本過濾:接收文本輸入流并對文本執行一些轉換,然後在發送到輸出流的過程。這種過濾通常是通過構造指令管道線

來完成的,其中來自一個指令的輸出被導入

或重定向

為下一個指令的輸入。管道的這種使用方式并不局限于文本流,盡管這是它的最常見用法。

通過管道将 stdout

導入到 stdin

在兩個指令之間使用管道 |

操作符将的一個指令的 stdout

指向第二個指令的 stdin。您可以通過添加更多的指令和管道操作符來構造更長的管道線。任何指令都可能包含選項或參數。許多指令使用連字元

(-) 取代檔案名作為一個參數,用于表示輸入來自stdin

而不是檔案。檢視手冊頁確定正确使用指令。構造由多個指令(每個指令都有特定的功能)組成的長管道線是在Linux

UNIX® 中用于完成任務的常見方法。在清單 10

的假設管道線中,

command2

command3

都帶有參數,但

command3

僅使用

-

參數表示來自 stdin

的輸入。

10. 通過管道從幾個指令導出輸出

command1 | command2 paramater1 | command3 parameter1 - parameter2 | command4      

需要說明的是,管道線僅将stdout

導向stdin。您不能使用2|

單獨導出stderr,至少使用我們目前所了解的工具還不能這樣做。如果stderr

已被重定向到stdout,那麼兩個流都會被通過管道導出。在清單11

中,我們展示了一個不太現實的

ls

指令,它有 4

個不是按字母順序出現的通配符參數,然後使用一個管道對包含正常和錯誤輸出内容的進行分類。

11. 使用管道導出兩個輸出流

[ian@echidna lpi103-4]$ ls y* x* z* u* q*      
ls: cannot access z*: No such file or directory      
ls: cannot access u*: No such file or directory      
ls: cannot access q*: No such file or directory      
xaa  xab  yaa  yab      
[ian@echidna lpi103-4]$ ls y* x* z* u* q*  2>&1 |sort      
ls: cannot access q*: No such file or directory      
ls: cannot access u*: No such file or directory      
ls: cannot access z*: No such file or directory      
xaa      
xab      
yaa      
yab      

系統中的管道的優點之一是,與其他流行的作業系統不同,它們的管道不涉及到中間檔案。第一個指令的stdout

沒有寫到一個檔案中,然後再由第二個指令讀取。在文章“

Linux,101:檔案和目錄管理

中,您學習了如何使用

tar

指令在一個步驟中歸檔和壓縮檔案。即使您使用的UNIX

系統的

tar

指令不支援使用

-z

(gzip)或

-j

(bzip2)進行壓縮也不成問題。您可以使用這樣的管道

bunzip2 -c somefile.tar.bz2 | tar -xvf -

完成該任務。

使用檔案而不是 stdout

開始管道線

在以上的管道線中,我們在開始時使用一些生成輸出的指令,然後通過管道線的每個階段導出輸出。如果我們要以現有的檔案開始,應該怎麼辦呢?許多指令都接受stdin

或檔案作為輸入,是以這不成問題。如果您有要求來自stdin

的輸出的過濾器,那麼可以考慮使用

cat

指令将檔案複制到stdout。不過,您可以對第一個指令使用輸入重定向,然後在剩餘的管道下中導出該指令的輸出,這是更加常見的解決方案。僅需使用<

操作符将第一個指令的stdin

重定向到需要處理的檔案。

使用輸出作為參數

在前面對管道線的讨論中,您學習了如何接受一個指令的輸出,并将它用作另一個指令的輸入。反過來,假設您想将一個指令或檔案的内容作為另一個指令的參數而不是輸入。管道線不能用于實作該目的。三種常見的解決辦法是:

xargs

帶有

-exec

選項的

find

指令替換

您将首先了解第一個解決辦法。我們曾經在清單 9

中建立了一個強制制表符,您可以從中看到指令替換的例子。可以在指令行上使用指令替換,但在腳本中使用它則更常見;您将在本系列的後續文章中更多地了解它和腳本。檢視我們的

xargs

xargs

指令讀取标準的輸入,然後使用參數作為輸入建構和執行指令。如果沒有給出指令,那麼将使用

echo

指令。清單 12

是使用我們的 text1

檔案的基礎例子,它包含 3

個行,每行隻有兩個單詞。

12. 使用

xargs

[ian@echidna lpi103-4]$ cat text1      
1 apple      
2 pear      
3 banana      
[ian@echidna lpi103-4]$ xargs<text1      
1 apple 2 pear 3 banana      

為什麼

xargs

隻有一行輸出?預設情況下,

xargs

在空格處中斷輸出,并且每個生成的标記都成為一個參數。不過,當

xargs

建構指令時,它将一次傳遞盡可能多的參數。您可以使用

-n

覆寫該行為,或使用

--max-args

參數。在清單13

中,我們使用了這兩種方法,并為使用

xargs

添加一個顯式的

echo

調用。

13. 使用

xargs

echo

[ian@echidna lpi103-4]$ xargs<text1 echo "args >"      
args > 1 apple 2 pear 3 banana      
[ian@echidna lpi103-4]$ xargs --max-args 3 <text1 echo "args >"      
args > 1 apple 2      
args > pear 3 banana      
[ian@echidna lpi103-4]$ xargs -n 1 <text1 echo "args >"      
args > 1      
args > apple      
args > 2      
args > pear      
args > 3      
args > banana      

如果輸入包含由單引号或雙引号保護的空格,或使用了斜杠進行轉義,那麼

xargs

将不在遇到這些空格時中斷。清單 14

顯示了這些空格點。

14. 使用帶引号的

xargs

[ian@echidna lpi103-4]$ echo '"4 plum"' | cat text1 -      
1 apple      
2 pear      
3 banana      
"4 plum"      
[ian@echidna lpi103-4]$ echo '"4 plum"' | cat text1 - | xargs -n 1      
1      
apple      
2      
pear      
3      
banana      
4 plum      

到目前為止,已經在指令的末尾添加了所有參數。如果您需要在這些參數後面再使用其他參數,可以使用

-I

選項指定一個替換字元串。如果

xargs

将要執行的指令包含有替換字元串,那麼将使用參數替換它。進行了替換之後,僅将參數傳遞給每個指令。不過,将從一整行輸出建立參數,而不僅是一個标記。您還可以使用

xargs

-L

選項讓指令将行當作參數看待,而不是預設的以單個空格分隔的标記。使用

-I

選項表示

-L 1

。清單 15

顯示了使用

-I

-L

選項的例子。

15. 使用帶有輸入行的

xargs

[ian@echidna lpi103-4]$ xargs -I XYZ echo "START XYZ REPEAT XYZ END" <text1      
START 1 apple REPEAT 1 apple END      
START 2 pear REPEAT 2 pear END      
START 3 banana REPEAT 3 banana END      
[ian@echidna lpi103-4]$ xargs -IX echo "<X><X>" <text2      
<9      plum><9 plum>      
<3      banana><3       banana>      
<10     apple><10       apple>      
[ian@echidna lpi103-4]$ cat text1 text2 | xargs -L2      
1 apple 2 pear      
3 banana 9 plum      
3 banana 10 apple      

盡管我們的例子為了便于示範使用了簡單的文本檔案,您很少看到包含這樣的輸入的

xargs

。您通常需要處理某些指令生成的大量檔案,這些指令包括

ls

find

grep

。清單 16

顯示了一種通過

xargs

将目錄清單傳遞到指令(比如

grep

)的方法。

16. 使用帶有多個檔案的

xargs

[ian@echidna lpi103-4]$ ls |xargs grep "1"      
text1:1 apple      
text2:10        apple      
xaa:1 apple      
yaa:1      

如果上一個例子中的一個或多個檔案名包含空格,那麼會發生什麼呢?如果您像清單 16

那樣使用該指令,那麼将得到一個錯誤。在實際情況中,檔案清單可能來自一些源,比如定制腳本或指令,而不是

ls

,或者您希望通過其他管道線階段傳遞它,以進一步進行過濾。是以您應該使用

grep "1" *

取代以上構造。

對于

ls

指令,您可以使用

--quoting-style

選項強制給導緻問題的檔案名加上引号或進行轉義。另外一種更好的解決辦法是使用

xargs

-0

選項,進而使用 null

字元串 (\0)

分隔輸入參數。盡管

ls

沒有提供使用 null

字元串分隔的檔案名作為輸出的選項,但許多指令都提供這樣的選項。

在清單 17

中,我們首先将 text1

複制到 “text 1”,然後顯示一些在

xargs

指令中使用包含空格的檔案名清單的方法。這些示例僅為了示範概念,因為

xargs

可能更加複雜。尤其是在最後一個例子中,

如果一些檔案名已經包含新行字元串,那麼将新行字元串轉換成 null

字元串将導緻錯誤。在本文的下一個部分中,我們将檢視另外一個更加健壯的解決方案,即使用

find

指令生成合适的以 null

字元串分隔的輸出。

清單 17.

檔案名中包含空格的

xargs

[ian@echidna lpi103-4]$ cp text1 "text 1"      
[ian@echidna lpi103-4]$ ls *1 |xargs grep "1" # error      
text1:1 apple      
grep: text: No such file or directory      
grep: 1: No such file or directory      
[ian@echidna lpi103-4]$ ls --quoting-style escape *1      
text1  text\ 1      
[ian@echidna lpi103-4]$ ls --quoting-style shell *1      
text1  'text 1'      
[ian@echidna lpi103-4]$ ls --quoting-style shell *1 |xargs grep "1"      
text1:1 apple      
text 1:1 apple      
[ian@echidna lpi103-4]$ # Illustrate -0 option of xargs      
[ian@echidna lpi103-4]$ ls *1 | tr '\n' '\0' |xargs -0 grep "1"      
text1:1 apple      
text 1:1 apple      

xargs

指令不會建構任意長度的指令。在Linux

核心2.26.3

之前,指令的長度是受限制的。針對某個包含大量名稱很長的檔案的目錄的指令,比如

rm somepath/*

,可能會失敗,傳回的消息表明參數清單太長。在更舊的Linux

系統或UNIX

系統上仍然存在該限制,是以了解如何使用

xargs

以處理這種問題非常有用。

--show-limits

選項顯示

xargs

的預設限制,然後使用

-s

選項将輸出指令的長度限制在允許的最大字元串數量之内。檢視手冊頁了解其他未能再次讨論的選項。

使用帶有

-exec

選項或

xargs

find

在文章 “

中,您學習例如如何使用

find

指令根據名稱、修改時間、大小或其他特征查找檔案。找到比對的檔案集之後,您通常希望對它們執行某些操作:删除、移動和重命名它們等。現在我們看一下

find

-exec

選項,其功能類似于使用

find

并通過管道将輸出指向

xargs

18. 使用

find

-exec

[ian@echidna lpi103-4]$ find text[12] -exec cat text3 {} \;      
This is a sentence.  This is a sentence.  This is a sentence.      
1 apple      
2 pear      
3 banana      
This is a sentence.  This is a sentence.  This is a sentence.      
9       plum      
3       banana      
10      apple      

與前面學習的

xargs

指令相比,它有幾個不同之處。

您必須使用 {}

标記檔案名在指令中的位置。它不是自動添加在末尾的。

您必須使用轉義後的分号終止該指令,比如 \;、';'

或 ";"

都行。

該指令對每個輸入檔案執行一次。

嘗試運作

findtext[12] |xargs cat text3

親自看看差別在哪裡。

現在,我将話題轉回到檔案名中的空格。在清單 19

中我們嘗試使用帶有

-exec

find

,而不是帶有

xargs

ls

19. 對包含空格的檔案名使用

find

-exec

[ian@echidna lpi103-4]$ find . -name "*1" -exec grep "1" {} \;      
1 apple      
1 apple      

到目前為止,一切進展順利。但是不是缺少了什麼?哪個檔案包含

grep

找到行?缺少了檔案名,因為

find

為每個檔案調用

grep

一次,而

grep

非常智能,能夠知道您是不是僅提供檔案名,您不需要它告訴您是哪個檔案。

我們也可以改為使用

xargs

,但我們已經看到了檔案名中包含空格時出現的問題。我們還提到

find

可以生成一個以 null

分隔符分隔的檔案名清單,這是

-print0

選項所起的作用。新的

find

可能使用加号(+)取代分号(;)作為分隔符,這允許

find

在一次調用指令時傳遞盡可能多的名稱,類似于

xargs

。在這種情況中,僅能使用{}

一次,并且它必須是該指令的最後一個參數。清單20

顯示了這兩種方法。

20. 對包含空格的檔案名使用

find

xargs

[ian@echidna lpi103-4]$ find . -name "*1" -print0 |xargs -0 grep "1"      
./text 1:1 apple      
./text1:1 apple      
[ian@echidna lpi103-4]$ find . -name "*1" -exec grep "1" {} +      
./text 1:1 apple      
./text1:1 apple      

一般而言,兩種方法都是有效的,選擇哪種方法由您決定。記住,使用管道導出包含未受保護的空格的内容将導緻問題,是以如果您要使用管道将輸出導出到

xargs

,請使用将

-print0

find

結合使用,并使用

-0

xargs

接收使用 null

分隔符分隔的輸入。其他指令,包括

tar

,也支援使用

-0

選項并用 null

分隔符分隔的輸入,是以應該對支援該選項的指令使用它,除非您能確定您的輸入清單不會造成問題。

最後,我們介紹對檔案清單進行操作。在執行删除或重命名檔案等重要操作之前,最好徹底地測試清單和仔細測試指令。進行良好的備份也是非常有價值的。

分離輸出

這個小節簡單地讨論另一個指令。有時候,您可能希望在螢幕上看到輸出,同時保留一個副本。盡管您可以将指令輸出重定向到一個視窗中的檔案,然後使用

tail -fn1

在另一個螢幕中跟蹤輸出來實作該目的,但使用

tee

指令要簡單得多。

您可以将

tee

和管道一起使用。對标準輸出而言,參數是一個或多個檔案。

-a

選項附加而非覆寫檔案。在前面關于管道的讨論中可以看到,必須先将 stderr

重定向到 stdout

,然後再重定向到

tee

,如果您需要同時儲存兩者的話。清單21

顯示

用于将輸出儲存到檔案 f1

和 f2

tee

21. 使用

tee 分離

stdout

[ian@echidna lpi103-4]$ ls text[1-3]|tee f1 f2      
text1      
text2      
text3      
[ian@echidna lpi103-4]$ cat f1      
text1      
text2      
text3      
[ian@echidna lpi103-4]$ cat f2      
text1      
text2      
text3      

繼續閱讀