Linux命令技巧

maojunxu發表於2014-05-08


技巧:
cat
合併檔案

您常常需要將幾個檔案處理成一個檔案並將這種處理的結果儲存到一個單獨的輸出檔案。
cat
“concatenate”的縮寫)命令在其輸入上接受一個或多個檔案並將它們作為一個單獨的檔案列印到它的輸出。例如,
cat chapter01 chapter02 chapter03 > book
將三個chapterXX
檔案儲存在一個單獨的book
檔案中。

輸入檔案按照它們在
cat
命令後的排列順序被列印,因此,要調換資訊的順序,就必須先調換輸入檔案的順序。此外,當需要處理的檔案數目對於您來說過大而無法手工輸入這些檔案的名稱
時,您可以使用萬用字元,如在
cat chapter* > book中使用的那樣,記住,檔名將會按升序排列。當您突然發現chapter13
被髮送到輸出中時會在chapter2之前,而會在
chapter02之後時,這會引起很有意思的問題。


cat
的輸出沒有被重定向到一個檔案或另一個命令的標準輸出時,
cat
表現出來的行為與多數命令列工具一樣,即將其輸出傳送到控制檯。
這意味著您可以使用
cat
來顯示檔案;例如,您可以使用
cat /etc/passwd來顯示系統密碼檔案的內容。為方便起見,您應該用less檢視大檔案,如在
less /etc/passwd中那樣(您可以通過輸入
man less
學習更多關於less
的知識)。

儘管
cat
主要用於合併檔案,您還可以將它用於輸入的簡單自動處理。例如,您可以使用
一個單獨的空白行來除去多行空白行(使用
-s
選項),這是一個在您將原始碼公諸於世前進行清除工作的好辦法。遺憾的是,
cat
並沒有用於一次清除所有空白行的選項。但這並不是什麼大問題,因為您
可以使用方便的
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
頁面的原始檔,特別是那些由指令碼生成的插入了不必要新行的原始檔,以及那些包含大型條件結構(其各個項之間已經用空行分開)的原始檔來說,空白行緊縮是一個方便的技巧。

cat
的另外一個重要的功能是它可以對行進行編號。這種功能對於程式文件的
編制以及法律和科學文件的編制很方便。列印在左邊的行號使得參考文件的某一部分變得容易。這在程式設計、科學研究、業務報告或甚至是立法工作中都是非常重要的。
對行進行編號功能有兩個選項:
-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




 

cat
還可以在您檢視包含如製表符這樣的非列印字元的檔案時起幫助作用。
您可以用以下選項來顯示製表符:



-T
將製表符顯示為
^I

-v
顯示非列印字元,除了換行符和製表符,它們使用各自效果相當的控制序列
例如,當您處理一個在 Windows
系統中生成的檔案時,這個檔案將使用 Control-M
^M
)來標記行的結束。對於程式碼大於 127
的字元,它們的前面將會被加上
M-
(表示“meta”),這與其它系統中在字元前面加上
Alt-
相當。

-E
在每一行的結束處新增美元符(
$
)。

清單3.
顯示非列印字元




$ cat -t /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.

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

^IFontPath^I”unix/:7100″

EndSection

$ 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 file
來顯示後 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
的原因,如下所示)。

清單 1.
簡單排序



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



 

有關 sort
tsort
的更多資訊

通過開啟
有關排序操作的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
(使用三個字母的月份名稱縮寫:JANFEBMAR
來對行進行排序)和
-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 < file
中時,它檢查輸入檔案是否已進行了排序(您也可以使用其它選項),如果已進行了排序,則報告一個錯誤。這樣,在處理可能需要花很長時間進行排序的大型檔案之前,可以很方便地對其進行檢查。當您將
-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 `
` `
` < macfile > unixfile

UNIX -> Mac
tr `
` `
` < unixfile > macfile

Microsoft DOS/Windows
約定,文字的每行以回車字元並後跟換行符結束。為了糾正這個問題,可以使用下列命令:


DOS -> UNIX
tr -d `
` < dosfile > unixfile

UNIX -> DOS:在這種情況下,需要用
awk
,因為
tr 不能插入兩個字元來替換一個字元。要使用的 awk
命令為 awk `{ print $0"
" }` < unixfile > dosfile

另外,當您需要對文字檔案做一些簡單的整理工作(如用
tr -d ` `除去製表符,用
tr -s ` `
除去多餘的空格,或者用
tr -d `
`
將分開的幾行合成一行)時,會需要用tr。同樣,可以在vi
內使用所有這些命令;只要記住:在tr命令前要加上您希望處理的行範圍和感嘆號(!),如
1,$!tr -d ` `(美元符號表示最後一行)中所示。

下次,我們將討論
uniq。到那時見!

 

技巧:
uniq
除去重複

進行排序之後,您會發現有些行是重複的。有時候該重複資訊是不需要的,可以將它除去以節省磁碟空間。不必對文字行進行排序,但是您應當記住
uniq
在讀取行時會對它們進行比較並將只除去兩個或更多的連續行。下面的示例說明了它實際上是如何工作的:

清單
1.
uniq
除去重複行

 




                              

       
$ 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!

       
$ sort happybirthday.txt | uniq

Happy Birthday Dear Tux!

Happy Birthday to You!

     




 

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

如果您希望您的工作輕鬆點,比如只顯示唯一的或重複的行,那麼該怎麼辦呢?您可以用
-u
(唯一)和
-d
(重複)選項來做到這一點,例如:

清單
2.
使用 -u
-d
選項

 




                              

       
$ sort happybirthday.txt | uniq -u

Happy Birthday Dear Tux!

       
$ sort happybirthday.txt | uniq -d

Happy Birthday to You!

     




 

您還可以用
-c
選項從uniq
中獲取一些統計資訊:

清單
3.
使用 -c
選項

 




                              

       
$ sort happybirthday.txt | uniq -uc

     
1 Happy Birthday Dear Tux!

       
$ sort happybirthday.txt | uniq -dc

     
3 Happy Birthday to You!

     




 

就算
uniq
對完整的行進行比較,它仍然會很有用,但是那並非該命令的全部功能。特別方便的是:使用
-f
選項,後面跟著要跳過的欄位數,它能夠跳過給定數目的欄位。當您檢視系統日誌時這非常有用。通常,某些項要被複制許多次,這使得檢視日誌很難。使用簡單的
uniq
無法完成任務,因為每一項都以不同的時間戳記開頭。但是如果您告訴它跳過所有的時間欄位,您的日誌一下子就會變得更加便於管理。試一試
uniq -f 3 /var/log/messages,親眼看看。

還有另一個選項
-s
,它的功能就像-f
一樣,但是跳過給定數目的字元。您可以一起使用
-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/Vim
的基本命




功能



命令



說明





啟動



vi
開啟 Vi/Vim
vi <file> 開啟 Vi/Vim
並載入檔案 <file>



 



退出



ZQ
無條件退出

:q!
無條件退出

ZZ
存檔並退出

:wq
存檔並退出



 



儲存部分檔案



:m,nw <file>
m
行到 n
行部分的內容儲存到檔案 <file>


:m,nw >> <file>
m
行到 n
行的內容新增到檔案 <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
使用手冊。

   Vi/Vim
中操作單位有很多,按從小到大的順序為(括號內為相應的操作命令):字元(hl
單詞 (wWbBe)
(jk0^$:n)
句子(()
段落({}
(HML)
頁(Ctrl-fCtrl-bCtrl-uCtrl-d)
檔案(Ggg:0:$)。

  具體命令解釋如下:


2.
移動游標的基本命




操作單位



命令



說明





字元



h
左移一字元

l
右移一字元



 



單詞



w/W
移動到下一單詞的開頭

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

e/E
移動到游標所在單詞的末尾



WBE
命令操作的單詞是以空白字元(空格、Tab)分隔的字串,比如字串“str1-str2
str3-str4”
,對 WBE
命令來說是兩個單詞,而對 wbe
命令來說則是四個單詞。







j
下移一行

k
上移一行

0
移到當前行開頭

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

$
移到當前行末尾

:n
移動到第 n



0
為數字零(zero





句子



)
移動到當前句子的末尾

(
移動到當前句子的開頭



 



段落



}
移動當前段落的末尾

{
移到當前段落的開頭



 





H
移動到螢幕的第一行

M
移動到螢幕的中間一行

L
移動到螢幕的最後一行



 





Ctrl-f
向前滾動一頁

Ctrl-b
向後滾動一頁

Ctrl-u
向前滾動半頁

Ctrl-d
向後滾動半頁



 



檔案



G
移動到檔案末尾

gg
移動到檔案開頭

:0
移動到檔案第一行

:$
移動到檔案最後一行



0
為數字零(zero




 

  除了這些基本單位之外,還有
%
(跳轉到與之匹配的括號處),`.(跳轉到最近修改過的位置並定位編輯點),`.(跳轉到最近修改過的位置但不定位編輯點)這三個命令也非常重要,在Vi/Vim
中靈活使用會極大提高效率。%除用於游標移動之後,還可用於檢測原始碼中各種括號的匹配情況。


回頁首

文字編輯

  與游標移動一樣,Vi/Vim
中關於編輯操作的命令也比較多,但操作單位要比移動游標少得多。按從小到大的順序為(括號內為相應的操作命令):字元
xcsria
單詞 (cwcWcbcBdwdWdbdB)
(ddd0d$IAoO)
句子(()
段落({})。這些操作單位有些可以加操作次數。操作物件的範圍計算公式為:操作範圍
=
操作次數*
操作單位。比如:d3w
命令刪除三個單詞,10dd命令刪除十行。

  具體命令解釋如下:


3.
文字編輯的基本命




操作單位



命令



說明





字元



x
刪除游標位置的字元

c
更改當前字元並進入插入模式

s
替換游標位置的字元並進入插入模式

r
替換游標位置的字元但不進入插入模式

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

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



 



單詞



cw/cW
刪除當前單詞從游標開始的部分並進入插入模式

cb/cB
刪除當前單詞從游標所在位置至單詞開始的部分並進入插入模式

dw/dW
刪除當前單詞從游標開始的部分但不進入插入模式

db/dB
刪除當前單詞從游標所在位置至單詞開始的部分但不進入插入模式



cWcBdWdB
命令操作的單詞是以空白字元(空格、Tab)分隔的字串,比如字串“str1-str2
str3-str4”
,對 cW,cB,dW,dB
命令來說是兩個單詞,而對 cwcbdwdb
命令來說則是四個單詞。







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
下的內碼




 


回頁首

文字搜尋與替換

   Vi/Vim
用於文字搜尋的主要有下面的三個基本命令:


5.
文字搜尋的基本命




功能



命令



說明





搜尋



/
在檔案中向前搜尋

?
在檔案中向後搜尋





可使用正規表示式

可配合運算元使用,比如 3/str
向前搜尋字串 str
並將游標移到第三個找到的串





搜尋下一個



n
搜尋下一個

N
反向搜尋下一個





可使用正規表示式

可配合運算元使用,比如命令 3N
反向搜尋第三個匹配的字串




 

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


6.
文字搜尋的高效命




功能



命令



說明





快速搜尋



*
在檔案中向前搜尋當前游標所在的單詞

#
在檔案中向後搜尋當前游標所在的單詞



非常快捷的搜尋命令





顯示搜尋命令歷史



q/
顯示搜尋命令歷史的視窗

q?
顯示搜尋命令歷史的視窗



可以選擇重用以前用過的搜尋查詢命令





查詢幫助



Shift-k
查詢游標所在命令或函式的 man
幫助





可以在不退出 Vi/Vim
情況下快速查詢命令或函式的使用方法;

q
鍵退出 man
幫助




 

  關於替換主是要結合搜尋使用行命令來實現,命令格式為:

    
:m,ns/str1/str2/g
m
行到 n
行中的字串 str1
全部替換為字串 str2

  在眾多使用正規表示式進行替換的命令中,平時需要掌握的一個命令是如何快速去除 ^M
字元。在 Linux/Unix
系統中編輯 Windows
作業系統中生成的檔案時,如果上傳時回車換行符處理不正確的話,用Vi/Vim
開啟經常會出現^M
字元。如果上傳的檔案是shell
指令碼的話,即使賦予了正確的執行許可權該指令碼還是無法執行。這也是各種檔案在Linux/Unix
Windows
中傳輸經常會出現問題的地方之一。其實,在Vi/Vim
中使用替換命令:1,$s/^M//g即可以很容易地快速去掉
^M
字元。這裡需要注意的是 ^M
是使用 Ctrl-vCtrl-m
輸入的。


回頁首

執行外部命令

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


7.
執行外部命令格




功能



命令



說明





執行外部命令



:!<cmd>
執行外部命令 <cmd>



在正常模式下輸入該命令





顯示命令列命令歷史



q:
顯示命令列命令歷史的視窗



可以選擇重用以前用過的命令列命令




 

  其中命令
q:
會顯示使用過的行命令歷史,可以從中選擇重用以前用過的命令。這對於需要重複應用那些複雜的命令來說,非常方便快捷。


回頁首

Vi/Vim
設定

   Vi/Vim
有很多內部變數,可以根據需要進行相應的設定。變數型別不同往往設定方式也不一樣,簡單的只要設定特定的變數名即可,複雜的則需要指定和分配一個顯式值來設定變數。在實際應用中,如果有需要,請參考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
檔案位於該使用者的主目錄下,檔名為.vimrcVi/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
101LPIC-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
檔案系統中的檔案)。您將在建立和修改硬連結和符號連結

一文(參見
學習 Linux101LPIC-1
路線圖
)中瞭解到有關符號連結的更多內容。型別之後是針對所有者、所有者所在組的成員、每一個成員的三組特權。這三個值分別表示使用者、組、組成員是否擁有讀 (r)、寫
(w) (x)
執行許可權。諸如
setuid 之類的使用者將在管理檔案許可權和所有權(參見
學習 Linux101LPIC-1
路線圖
)一文中介紹

下一個欄位是一個數字,告訴我們檔案的硬連結
的數量。我們已經介紹過,inode
包含有關檔案的資訊。檔案的目錄條目包含到檔案的
inode 的硬連結(或指標),因此列出的每個條目都應該至少擁有一個硬連結。目錄條目對 .
條目和每個子目錄條目使用另外的硬連結。因此我們可以從清單 2
中可以看到,使用
.. 表示的主目錄有大量子目錄,因此包含 35
個硬連結。

接下來兩個欄位分別為檔案的所有者和所有者的主組。某些系統,例如 Red Hat
Fedora
系統,在預設情況下為每個使用者提供單獨的組。在其他系統中,所有使用者可能位於一個或多個組中。

下一個欄位包含檔案的長度,以位元組為單位

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

最後一個欄位包含檔案或目錄的名稱

ls命令的
-i
選項將顯示 inode
號。您將在本文後面以及
建立和修改硬連結和符號連結(參見
學習
Linux
101LPIC-1
路線圖
)中再次見到有關
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
Windows
作業系統中,不會做出目標為當前目錄的預設假設

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 $?
0



 

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
被單獨應用到路徑名的每個組成中。您無法匹配 `/`,也不能把它包含在一個範圍中。您可以在指定多個檔案或目錄名時使用它,例如在
lscpmv
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]` 匹配名稱中不包含 23
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
互動的最佳方式是進行實踐,因此您需要多多嘗試這些萬用字元。注意,在使用
cpmv
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



 

Linux
系統同時記錄檔案修改
時間和檔案訪問
時間。這兩個時間也分別被稱為
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命令的手冊頁可以幫助您瞭解各種選項,我們無法在這篇簡短的介紹中一一介紹它們。


回頁首

識別檔案

檔名通常具有一個字尾,比如 gifjpeg
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

bzip2gzip命令使用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系統不支援原始的未壓縮檔案集所含的符號連結。

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


回頁首

歸檔檔案

tarcpio

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
命令

就其最簡單形式而言,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



 

gzip
壓縮將檔案大小減小到未壓縮大小的 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


回頁首

設定示例

在本文中,我們將使用在文章學習
Linux
101:文字流和過濾器

中建立的一些檔案練習命令。即使您沒有閱讀那篇文章或者沒有儲存所建立的檔案,也一樣能順利學習本文。我們首先在您的主目錄下建立一個名為lpi103-4
的子目錄並在其中建立必要的檔案。為此,在作為當前目錄的主目錄下開啟一個文字視窗,將清單1
中的內容複製到文字視窗並執行命令。完成之後就建立了您將要使用的lpi103-4
子目錄和檔案。

清單
1.
建立示例檔案



                               
mkdir -p lpi103-4 && cd lpi103-4 && {
echo -e "1 apple
2 pear
3 banana" > text1
echo -e "9	plum
3	banana
10	apple" > 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
2 pear
3 banana" > text1
> echo -e "9	plum
3	banana
10	apple" > 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
打字機或顯示終端,但現在更多是指圖形桌面上的文字視窗。

如果您已經學習了文章學習
Linux
101:文字流和過濾器
,那麼就熟悉本文的部分內容。

重定向輸出

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

n>

將輸出從檔案描述符
n
重定向到檔案。您必須具有該檔案的寫許可權。如果該檔案不存在,將建立它。如果該檔案已經存在,通常將覆蓋所有現有內容,並且沒有任何警告

n>>

還可以將輸出從檔案描述符
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。如果您已經學習了文章
學習
Linux
101:文字流和過濾器
,那麼您可能還會記得我們在
sort
uniq
小節中使用
tr
命令將 text1
檔案中的空格替換成製表符。在那個例子中我們使用來自
cat
命令的輸出為
tr
命令建立標準輸入。現在,我們沒有必要呼叫
cat,而是使用輸入重定向將空格轉換成製表符,如清單7
所示。

清單
7.
輸入重定向



                               
[ian@echidna lpi103-4]$ tr ` ` `	`<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.
使用
here-document
的輸入重定向



                               
[ian@echidna lpi103-4]$ ht=$(echo -en "	")
[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



 

在本系列的後續文章中,我們將更詳細地介紹命令替換和指令碼。檢視我們的
學習
Linux
101LPIC-1
路線圖

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


回頁首

建立管道

在文章
學習
Linux
101:文字流和過濾器

中,我們這樣描述文字過濾:接收文字輸入流並對文字執行一些轉換,然後在傳送到輸出流的過程。這種過濾通常是通過構造命令管道線
來完成的,其中來自一個命令的輸出被匯入
重定向
為下一個命令的輸入。管道的這種使用方式並不侷限於文字流,儘管這是它的最常見用法。

通過管道將 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



 

Linux
UNIX
系統中的管道的優點之一是,與其他流行的作業系統不同,它們的管道不涉及到中間檔案。第一個命令的stdout
沒有到一個檔案中,然後再由第二個命令讀取。在文章學習
Linux
101:檔案和目錄管理

中,您學習瞭如何使用
tar
命令在一個步驟中歸檔和壓縮檔案。即使您使用的UNIX
系統的
tar
命令不支援使用
-zgzip)或
-jbzip2)進行壓縮也不成問題。您可以使用這樣的管道

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

完成該任務。

使用檔案而不是 stdout開始管道線

在以上的管道線中,我們在開始時使用一些生成輸出的命令,然後通過管道線的每個階段匯出輸出。如果我們要以現有的檔案開始,應該怎麼辦呢?許多命令都接受stdin
或檔案作為輸入,因此這不成問題。如果您有要求來自stdin
的輸出的過濾器,那麼可以考慮使用
cat
命令將檔案複製到stdout。不過,您可以對第一個命令使用輸入重定向,然後在剩餘的管道下中匯出該命令的輸出,這是更加常見的解決方案。僅需使用<
操作符將第一個命令的stdin
重定向到需要處理的檔案。


回頁首

使用輸出作為引數

在前面對管道線的討論中,您學習瞭如何接受一個命令的輸出,並將它用作另一個命令的輸入。反過來,假設您想將一個命令或檔案的內容作為另一個命令的引數而不是輸入。管道線不能用於實現該目的。三種常見的解決辦法是:



xargs


帶有 -exec
選項的 find


命令替

您將首先了解第一個解決辦法。我們曾經在清單 9
中建立了一個強制製表符,您可以從中看到命令替換的例子。可以在命令列上使用命令替換,但在指令碼中使用它則更常見;您將在本系列的後續文章中更多地瞭解它和指令碼。檢視我們的
學習
Linux
101LPIC-1
路線圖

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

使用
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。您通常需要處理某些命令生成的大量檔案,這些命令包括
lsfind
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
字串 ( )
分隔輸入引數。儘管
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 `
` ` ` |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
命令

在文章學習
Linux
101:檔案和目錄管理

中,您學習例如如何使用
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
 




相關文章