(五)Linux之檔案與目錄管理以及文字處理

天然呆dull發表於2021-08-24

Linux之檔案與目錄管理

前言

在 Linux 作業系統中,所有的檔案和目錄都被組織成以一個根節點“/”開始的倒置的樹狀結構,為了方便管理和維護,Linux 系統採用了檔案系統層次標準,也稱為 FHS 標準,它規定了根目錄下各個目錄應該存在哪些型別的檔案(或子目錄),比如說,在 /bin 和 /sbin 目錄中存放的應該是可執行檔案,有關各個目錄存放檔案的型別,已在《Linux之目錄結構》中作了詳解介紹,這裡不再過多贅述。

絕對路徑與相對路徑說明:

  • 絕對路徑: 路徑的寫法,由根目錄 / 寫起,例如: /usr/share/doc 這個目錄。

  • 相對路徑: 路徑的寫法,不是由 / 寫起,例如由 /usr/share/doc 要到 /usr/share/man 底下時,可以寫成: cd ../man 這就是相對路徑的寫法。

一、目錄常用命令

常用處理目錄的命令:

  • cd(英文全拼:change directory):切換目錄
  • pwd(英文全拼:print work directory):顯示目前的目錄(絕對路徑)
  • ls(英文全拼:list files): 列出目錄及檔名
  • mkdir(英文全拼:make directory):建立一個新的目錄
  • rmdir(英文全拼:remove directory):刪除一個空的目錄
  • touch :建立檔案及修改檔案時間戳
  • cp(英文全拼:copy file): 複製檔案或目錄
  • rm(英文全拼:remove): 刪除檔案或目錄
  • mv(英文全拼:move file): 移動檔案與目錄,或修改檔案與目錄的名稱

可以使用 man [命令] 或者 help [命令] 來檢視各個命令的使用文件,如 :man cphelp cp

切換目錄 cd

cd 命令,是 Change Directory 的縮寫,用來切換工作目錄。

Linux 命令按照來源方式,可分為兩種,分別是 Shell 內建命令和外部命令。所謂 Shell 內建命令,就是 Shell 自帶的命令,這些命令是沒有執行檔案的;而外部命令就是由程式設計師單獨開發的,所以會有命令的執行檔案。Linux 中的絕大多數命令是外部命令,而 cd 命令是一個典型的 Shell 內建命令,所以 cd 命令沒有執行檔案所在路徑。

cd 命令的基本格式如下:

[root@localhost ~]# cd [相對路徑或絕對路徑]

除此之外,cd 命令後面可以跟一些特殊符號,表達固定的含義,如 表 所示:

特殊符號 作 用
~ 代表當前登入使用者的主目錄
~使用者名稱 表示切換至指定使用者的主目錄
- 代表上次所在目錄
. 代表當前目錄
.. 代表上級目錄

它們的用法分別是:

  1. [root@localhost vbird]# cd ~
    #表示回到自己的主目錄,對於 root 使用者,其主目錄為 /root
    [root@localhost ~]# cd
    #沒有加上任何路徑,也代表回到當前登入使用者的主目錄
    [root@localhost ~]# cd ~vbird
    #代表切換到 vbird 這個使用者的主目錄,亦即 /home/vbird
    
  2. [root@localhost ~]# cd ..
    #表示切換到目前的上一級目錄,亦即是 /root 的上一級目錄的意思;
    

    需要注意的是,在 Linux 系統中,根目錄確實存在 .(當前目錄)以及 ..(當前目錄的父目錄)兩個目錄,但由於根目錄是最頂級目錄,因此根目錄的 .. 和 . 的屬性和許可權完全一致,也就是說,根目錄的父目錄是自身。

  3. [root@localhost /]# cd -
    #表示回到剛剛的那個目錄
    

如果你仔細觀察的話,不難發現,其實在 [root@localhost ~] 中,就已經指明瞭當前所在的目錄,通常剛登陸時會位於自己的主目錄中,而 ~ 就表示主目錄,因此也就有了通過使用 cd ~ 可以回到自己的主目錄

顯示當前路徑 pwd

pwd 命令,是 Print Working Directory (列印工作目錄)的縮寫,功能是顯示使用者當前所處的工作目錄(絕對路徑)。該命令的基本格式為:

[root@localhost ~]# pwd

【例 1】

[root@localhost ~]# whoami
root
[root@localhost ~]# pwd
/root

whoami 命令用於確定當前登陸的使用者,後續會做詳細介紹。可以看到,root 使用者當前所在目錄是它的主目錄 /root。

檢視目錄下檔案 ls

ls 命令,list 的縮寫,是最常見的目錄操作命令,其主要功能是顯示當前目錄下的內容。此命令的基本格式為:

[root@localhost ~]# ls [選項] 目錄名稱

下表 列出了 ls 命令常用的選項以及各自的功能:

選項 功能
-a 顯示全部的檔案,包括隱藏檔案(開頭為 . 的檔案)也一起羅列出來,這是最常用的選項之一。
-A 顯示全部的檔案,連同隱藏檔案,但不包括 . 與 .. 這兩個目錄。
-d 僅列出目錄本身,而不是列出目錄內的檔案資料。
-f ls 預設會以檔名排序,使用 -f 選項會直接列出結果,而不進行排序。
-F 在檔案或目錄名後加上檔案型別的指示符號,例如,* 代表可執行檔案,/ 代表目錄,= 代表 socket 檔案,| 代表 FIFO 檔案。
-h 以人們易讀的方式顯示檔案或目錄大小,如 1KB、234MB、2GB 等。
-i 顯示 inode 節點資訊。
-l 使用長格式列出檔案和目錄資訊。
-n 以 UID 和 GID 分別代替檔案使用者名稱和群組名顯示出來。
-r 將排序結果反向輸出,比如,若原本檔名由小到大,反向則為由大到小。
-R 連同子目錄內容一起列出來,等於將該目錄下的所有檔案都顯示出來。
-S 以檔案容量大小排序,而不是以檔名排序。
-t 以時間排序,而不是以檔名排序。
--color=never --color=always --color=auto never 表示不依據檔案特性給予顏色顯示。 always 表示顯示顏色,ls 預設採用這種方式。 auto 表示讓系統自行依據配置來判斷是否給予顏色。
--full-time 以完整時間模式 (包含年、月、日、時、分)輸出
--time={atime,ctime} 輸出 access 時間或改變許可權屬性時間(ctime),而不是內容變更時間。

注意,當 ls 命令不使用任何選項時,預設只會顯示非隱藏檔案的名稱,並以檔名進行排序,同時會根據檔案的具體型別給檔名配色(藍色顯示目錄,白色顯示一般檔案)。除此之外,如果想使用 ls 命令顯示更多內容,就需要使用上表 相應的選項。

【例 1】

[root@www ~]# ls -al ~
total 156
drwxr-x---  4 root root  4096 Sep 24 00:07 .
drwxr-xr-x 23 root root  4096 Sep 22 12:09 ..
-rw-------  1 root root  1474 Sep  4 18:27 anaconda-ks.cfg
-rw-------  1 root root   955 Sep 24 00:08 .bash_history
-rw-r--r--  1 root root    24 Jan  6  2007 .bash_logout
-rw-r--r--  1 root root   191 Jan  6  2007 .bash_profile
-rw-r--r--  1 root root   176 Jan  6  2007 .bashrc
drwx------  3 root root  4096 Sep  5 10:37 .gconf
-rw-r--r--  1 root root 42304 Sep  4 18:26 install.log
-rw-r--r--  1 root root  5661 Sep  4 18:25 install.log.syslog

通過使用 -a,你會看到以 . 為開頭的幾個檔案,以及目錄檔案 (.)、(..)、.gconf 等等,這些都是隱藏的目錄和檔案。其中,目錄檔名以藍色顯示,一般檔案以白色顯示。

注意,Linux 系統中,隱藏檔案不是為了把檔案藏起來不讓其他使用者找到,而是為了告訴使用者這些檔案都是重要的系統檔案,如非必要,不要亂動!所以,不論是 Linux 還是 Windows 都可以非常簡單地査看隱藏檔案,只是在 Windows 中絕大多數的病毒和木馬都會把自己變成隱藏檔案,給使用者帶來了錯覺,以為隱藏檔案是為了不讓使用者發現。

不僅如此,這裡的 ls 命令還使用了 -l 選項,因此才顯示出了檔案的詳細資訊,此選項顯示的這 7 列的含義分別是:

  1. 第一列:規定了不同的使用者對檔案所擁有的許可權,具體許可權的含義將在後續章節中講解。
  2. 第二列:引用計數,檔案的引用計數代表該檔案的硬連結個數,而目錄的引用計數代表該目錄有多少個一級子目錄。
  3. 第三列:所有者,也就是這個檔案屬於哪個使用者。預設所有者是檔案的建立使用者。
  4. 第四列:所屬組,預設所屬組是檔案建立使用者的有效組,一般情況下就是建立使用者的所在組。
  5. 第五列:大小,預設單位是位元組。
  6. 第六列:檔案修改時間,檔案狀態修改時間或檔案資料修改時間都會更改這個時間,注意這個時間不是檔案的建立時間。
  7. 第七列:檔名或目錄名。

【例 2】
如果我們想檢視某個目錄的詳細資訊,例如:

[root@localhost ~]# ls -l /root/
總用量44
-rw-------.1 root root 1207 1 月 14 18:18 anaconda-ks.cfg
-rw-r-r--.1 root root 24772 1 月 14 18:17 install.log
-rw-r-r--.1 root root 7690 1 月 14 18:17 install.log.syslog

這個命令會顯示目錄下的內容,而不會顯示這個目錄本身的詳細資訊。如果想顯示目錄本身的資訊,就必須加入 "-d" 選項。

[root@localhost ~]# ls -ld /root/
dr-xr-x---.2 root root 4096 1 月 20 12:30 /root/

【例 3】
"ls-l" 顯示的檔案大小是位元組,但是我們更加習慣的是千位元組用 KB 顯示,兆位元組用 MB 顯示,而 "-h" 選項就是按照人們習慣的單位顯示檔案大小的,例如:

[root@localhost ~]# ls -lh
總用量44K
-rw-------.1 root root 1.2K 1 月 14 18:18 anaconda-ks.cfg
-rw-r-r--.1 root root 25K 1 月 14 18:17 install.log
-rw-r-r--.1 root root 7.6K 1 月 14 18:17 install.log.syslog

建立目錄(資料夾) mkdir

mkdir 命令,是 make directories 的縮寫,用於建立新目錄,此命令所有使用者都可以使用。

mkdir 命令的基本格式為:

[root@localhost ~]# mkdir [-mp] 目錄名
  • -m 選項用於手動配置所建立目錄的許可權,而不再使用預設許可權。
  • -p 選項遞迴建立所有目錄,以建立 /home/test/demo 為例,在預設情況下,你需要一層一層的建立各個目錄,而使用 -p 選項,則系統會自動幫你建立 /home、/home/test 以及 /home/test/demo。

【例 1】建立目錄

[root@localhost ~]#mkdir cangls
[root@localhost ~]#ls
anaconda-ks.cfg cangls install.log install.log.syslog

我們建立一個名為 cangls 的目錄,通過 ls 命令可以檢視到這個目錄已經建立。注意,我們在建立目錄的時候使用的是相對路徑,所以這個目錄被建立到當前目錄下。

【例 2】使用 -p 選項遞迴建立目錄

[root@localhost ~]# mkdir lm/movie/jp/cangls
mkdir:無法建立目錄"lm/movie/jp/cangls":沒有那個檔案或目錄
[root@localhost ~]# mkdir -p lm/movie/jp/cangls
[root@localhost ~]# ls
anaconda-ks.cfg cangls install.log install.log.syslog lm
[root@localhost ~]# ls lm/
movie
#這裡只檢視一級子目錄,其實後續的jp目錄、cangls目錄都已經建立

【例 3】使用 -m 選項自定義目錄許可權

[root@localhost ~]# mkdir -m 711 test2
[root@localhost ~]# ls -l
drwxr-xr-x  3 root  root 4096 Jul 18 12:50 test
drwxr-xr-x  3 root  root 4096 Jul 18 12:53 test1
drwx--x--x  2 root  root 4096 Jul 18 12:54 test2

刪除空目錄 rmdir

和 mkdir 命令(建立空目錄)恰好相反,rmdir(remove empty directories 的縮寫)命令用於刪除空目錄,此命令的基本格式為:

[root@localhost ~]# rmdir [-p] 目錄名

-p 選項用於遞迴刪除空目錄。

【例 1】

通過學習 mkdir 命令我們知道,使用 mkdir -p 可以實現遞迴建立目錄,同樣地,rmdir 命令可以使用 -p 選項遞迴刪除目錄。例如:

[root@localhost ~]# rmdir -p lm/movie/jp/cangls

注意,此方式先刪除最低一層地目錄(這裡先刪除 cangls),然後逐層刪除上級目錄,刪除時也需要保證各級目錄是空目錄。

【例 2】

rmdir 命令的作用十分有限,因為只能刪除空目錄,所以一旦目錄中有內容,就會報錯。例如:

[root@localhost # mkdir test
#建立測試目錄
[root@localhost ~]# touch test/boduo
[root@localhost ~]# touch test/longze
#在測試目錄中建立兩個檔案
[root@localhost ~]# rmdir test
rmdir:刪除"test"失敗:目錄非空

這個命令比較"笨",所以並不常用。一般用 rm

建立檔案及修改檔案時間戳 touch

既然知道了如何在 Linux 系統中建立目錄,接下來你可能會想在這些目錄中建立一些檔案,可以使用 touch 命令。

需要注意的是,touch 命令不光可以用來建立檔案(當指定操作檔案不存在時,該命令會在當前位置建立一個空檔案),此命令更重要的功能是修改檔案的時間引數(但當檔案存在時,會修改此檔案的時間引數)。

Linux 系統中,每個檔案主要擁有 3 個時間引數(通過 stat 命令進行檢視),分別是檔案的訪問時間、資料修改時間以及狀態修改時間:

  • 訪問時間(Access Time,簡稱 atime):只要檔案的內容被讀取,訪問時間就會更新。例如,使用 cat 命令可以檢視檔案的內容,此時檔案的訪問時間就會發生改變。
  • 資料修改時間(Modify Time,簡稱 mtime):當檔案的內容資料發生改變,此檔案的資料修改時間就會跟著相應改變。
  • 狀態修改時間(Change Time,簡稱 ctime):當檔案的狀態發生變化,就會相應改變這個時間。比如說,如果檔案的許可權或者屬性發生改變,此時間就會相應改變。

touch 命令的基本格式如下:

[root@localhost ~]# touch [選項] 檔名

選項:

  • -a:只修改檔案的訪問時間;
  • -c:僅修改檔案的時間引數(3 個時間引數都改變),如果檔案不存在,則不建立新檔案。
  • -d:後面可以跟欲修訂的日期,而不用當前的日期,即把檔案的 atime 和 mtime 時間改為指定的時間。
  • -m:只修改檔案的資料修改時間。
  • -t:命令後面可以跟欲修訂的時間,而不用目前的時間,時間書寫格式為 YYMMDDhhmm

可以看到,touch 命令可以只修改檔案的訪問時間,也可以只修改檔案的資料修改時間,但是不能只修改檔案的狀態修改時間。因為,不論是修改訪問時間,還是修改檔案的資料時間,對檔案來講,狀態都會發生改變,即狀態修改時間會隨之改變(更新為操作當前檔案的真正時間)。

【例 1】 touch 命令建立檔案

[root@localhost ~]#touch bols
#建立名為 bols 的空檔案

【例 2】 在例 1 的基礎上修改檔案的訪問時間

[root@localhost ~]#ll --time=atime bols
#檢視檔案的訪問時間
-rw-r--r-- 1 root root 0 Sep 25 21:23 bols
#檔案上次的訪問時間為 9 月 25 號 21:23
[root@localhost ~]#touch bols
[root@localhost ~]#ll --time=atime bols
-rw-r--r-- 1 root root 0 May 15 16:36 bols
#而如果檔案已經存在,則也不會報錯,只是會修改檔案的訪問時間。

【例 3】 修改 bols 檔案的 atime 和 mtime

[root@localhost ~]# touch -d "2017-05-04 15:44" bols
[root@localhost ~]# ll bols; ll --time=atime bols; ll --time=ctime bols
-rw-r--r-- 1 root root 0 May 4 2017 bols
-rw-r--r-- 1 root root 0 May 4 2017 bols
-rw-r--r-- 1 root root 0 Sep 25 21:40 bols
#ctime不會變為設定時間,但更新為當前伺服器的時間

複製檔案和目錄 cp

cp 命令,主要用來複制檔案和目錄,同時藉助某些選項,還可以實現複製整個目錄,以及比對兩檔案的新舊而予以升級等功能。

cp 命令的基本格式如下:

[root@localhost ~]# cp [選項] 原始檔 目標檔案

選項:

  • -a:相當於 -d、-p、-r 選項的集合,這幾個選項我們一一介紹;
  • -d:如果原始檔為軟連結(對硬連結無效),則複製出的目標檔案也為軟連結;
  • -i:詢問,如果目標檔案已經存在,則會詢問是否覆蓋;
  • -l:把目標檔案建立為原始檔的硬連結檔案,而不是複製原始檔;
  • -s:把目標檔案建立為原始檔的軟連結檔案,而不是複製原始檔;
  • -p:複製後目標檔案保留原始檔的屬性(包括所有者、所屬組、許可權和時間);
  • -r:遞迴複製,用於複製目錄;
  • -u:若目標檔案比原始檔有差異,則使用該選項可以更新目標檔案,此選項可用於對檔案的升級和備用。

需要注意的是,原始檔可以有多個,但這種情況下,目標檔案必須是目錄才可以。

這裡的軟連結,類似於 Windows 系統中的快捷方式,而硬連結則是透過檔案系統的 inode 號產生一個新的檔名。無論是複製軟連結還是硬連結,都不是複製原始檔。有關軟連結和硬連結更詳細的介紹,可往下學習 Linux 連結概念

【例 1】cp 命令基本用法
cp 命令既可以複製檔案,也可以複製目錄。我們先來看看如何複製檔案,例如:

[root@localhost ~]# touch cangls
#建立原始檔
[root@localhost ~]# cp cangls /tmp/
#把原始檔不改名複製到 /tmp/ 目錄下

如果需要改名複製,則命令如下:

[root@localhost ~]# cp cangls /tmp/bols
#改名複製

如果複製的目標位置已經存在同名的檔案,則會提示是否覆蓋,因為 cp 命令預設執行的是“cp -i”的別名,例如:

[root@localhost ~]# cp cangls /tmp/
cp:是否覆蓋"/tmp/cangls"?y
#目標位置有同名檔案,所以會提示是否覆蓋

接下來我們看看如何複製目錄,其實複製目錄只需使用“-r”選項即可,例如:

[root@localhost ~]# mkdir movie
#建立測試目錄
[root@localhost ~]# cp -r /root/movie/ /tmp/
#目錄原名複製

【例 2】複製軟連結檔案
如果原始檔不是一個普通檔案,而是一個軟連結檔案,那麼是否可以複製軟連結的屬性呢?我們試試:

[root@localhost ~]# ln -s /root/cangls /tmp/cangls_slink
#建立一個測試軟連結檔案/tmp/cangls_slink
[root@localhost ~]# ll /tmp/cangls_slink
lrwxrwxrwx 1 root root 12 6 月 14 05:53 /tmp/cangls_slink -> /root/cangls
#原始檔本身就是一個軟連結檔案
[root@localhost ~]# cp /tmp/cangls_slink /tmp/cangls_t1
#複製軟連結檔案,但是不加"-d"選項
[root@localhost ~]# cp -d /tmp/cangls_slink /tmp/cangls_t2
#複製軟連結檔案,加入"-d"選項
[root@localhost ~]# ll /tmp/cangls_t1 /tmp/cangls_t2
-rw-r--r-- 1 root root 0 6月 14 05:56 /tmp/cangls_t1
#會發現不加"-d"選項,實際複製的是軟連結的原始檔,而不是軟連結檔案
lrwxrwxrwx 1 root root 12 6 月 14 05:56/tmp/ cangls_t2-> /root/cangls
#而如果加入了"-d"選項,則會複製軟連結檔案

這個例子說明,如果在複製軟連結檔案時不使用 "-d" 選項,則 cp 命令複製的是原始檔,而不是軟連結檔案;只有加入了 "-d" 選項,才會複製軟連結檔案。請大家注意,"-d" 選項對硬連結是無效的。

【例 3】保留原始檔屬性複製
我們發現,在執行復制命令後,目標檔案的時間會變成複製命令的執行時間,而不是原始檔的時間。例如:

[root@localhost ~]# cp /var/lib/mlocate/mlocate.db /tmp/
[root@localhost ~]# ll /var/lib/mlocate/mlocate.db
-rw-r-----1 root slocate2328027 6月 14 02:08/var/lib/mlocate/mlocate.db
#注意原始檔的時間和所屬組
[root@localhost ~]#ll /tmp/mlocate.db
-rw-r----- 1 root root2328027 6 月 14 06:05/tmp/mlocate.db
#由於複製命令由root使用者執行,所以目標檔案的所屬組為了root,而且時間也變成了複製命令的執行時間

而當我們執行備份、曰志備份的時候,這些檔案的時間可能是一個重要的引數,這就需執行 "-p" 選項了。這個選項會保留原始檔的屬性,包括所有者、所屬組和時間。例如:

[root@localhost ~]# cp -p /var/lib/mlocate/mlocate.db /tmp/mlocate.db_2
#使用"-p"選項
[root@localhost ~]# ll /var/lib/mlocate/mlocate.db /tmp/mlocate.db_2
-rw-r----- root slocate 2328027 6月 14 02:08 /tmp/mlocate.db_2
-rw-r----- root slocate 2328027 6月 14 02:08 /var/lib/mlocate/mlocate.db
#原始檔和目標檔案的所有屬性都一致,包括時間

我們之前講過,"-a" 選項相當於 "-d、-p、-r" 選項,這幾個選項我們已經分別講過了。所以,當我們使用 "-a" 選項時,目標檔案和原始檔的所有屬性都一致,包括原始檔的所有者,所屬組、時間和軟連結性。使用 "-a" 選項來取代 "-d、-p、-r" 選項更加方便。

【例 4】 "-l" 和 "-s" 選項
我們如果使用 "-l" 選項,則目標檔案會被建立為原始檔的硬連結;而如果使用了 "-s" 選項,則目標檔案會被建立為原始檔的軟連結。

這兩個選項和 "-d" 選項是不同的,"d" 選項要求原始檔必須是軟連結,目標檔案才會複製為軟連結;而 "-l" 和 "-s" 選項的原始檔只需是普通檔案,目標檔案就可以直接複製為硬連結和軟連結。例如:

[root@localhost ~]# touch bols
#建立測試檔案
[root@localhost ~]# ll -i bols
262154-rw-r--r-- 1 root root 0 6月 14 06:26 bols
#原始檔只是一個普通檔案,而不是軟連結檔案
[root@localhost ~]# cp -l /root/bols /tmp/bols_h
[root@localhost ~]# cp -s /root/bols /tmp/bols_s
#使用"-l" 和"-s"選項複製
[root@localhost ~]# ll -i /tmp/bols_h /tmp/bols_s
262154-rw-r--r-- 2root root 0 6 月 14 06:26/tmp/bols_h
#目標檔案 /tmp/bols_h 為原始檔的硬連結檔案
932113 lrwxrwxrwx 1 root root 10 6 月 14 06:27/tmp/bols_s -> /root/bols
#目標檔案 /tmp/bols_s 為原始檔的軟連結檔案

刪除檔案或目錄 rm (刪除檔案三思而後行)

rm 是強大的刪除命令,它可以永久性地刪除檔案系統中指定的檔案或目錄。在使用 rm 命令刪除檔案或目錄時,系統不會產生任何提示資訊。此命令的基本格式為:

[root@localhost ~]# rm[選項] 檔案或目錄

選項:

  • -f:強制刪除(force),和 -i 選項相反,使用 -f,系統將不再詢問,而是直接刪除目標檔案或目錄。
  • -i:和 -f 正好相反,在刪除檔案或目錄之前,系統會給出提示資訊,使用 -i 可以有效防止不小心刪除有用的檔案或目錄。
  • -r:遞迴刪除,主要用於刪除目錄,可刪除指定目錄及包含的所有內容,包括所有的子目錄和檔案。

注意,rm 命令是一個具有破壞性的命令,因為 rm 命令會永久性地刪除檔案或目錄,這就意味著,如果沒有對檔案或目錄進行備份,一旦使用 rm 命令將其刪除,將無法恢復,因此,尤其在使用 rm 命令刪除目錄時,要慎之又慎。

【例 1】基本用法。
rm 命令如果任何選項都不加,則預設執行的是"rm -i 檔名",也就是在刪除一個檔案之前會先詢問是否刪除。例如:

[root@localhost ~]# touch cangls
[root@localhost ~]# rm cangls
rm:是否刪除普通空檔案"cangls"?y
#刪除前會詢問是否刪除

【例 2】 刪除目錄。
如果需要刪除目錄,則需要使用"-r"選項。例如:

[root@localhost ~]# mkdir -p /test/lm/movie/jp
#遞迴建立測試目錄
[root@localhost ~]# rm /test
rm:無法刪除"/test/": 是一個目錄
#如果不加"-r"選項,則會報錯
[root@localhost ~]# rm -r /test
rm:是否進入目錄"/test"?y
rm:是否進入目錄"/test/lm/movie"?y
rm:是否刪除目錄"/test/lm/movie/jp"?y
rm:是否刪除目錄"/test/lm/movie"?y
rm:是否刪除目錄"/test/lm"?y
rm:是否刪除目錄"/test"?y
#會分別詢問是否進入子目錄、是否刪除子目錄

大家會發現,如果每級目錄和每個檔案都需要確認,那麼在實際使用中簡直是災難!

【例 3】強制刪除。
如果要刪除的目錄中有 1 萬個子目錄或子檔案,那麼普通的 rm 刪除最少需要確認 1 萬次。所以,在真正刪除檔案的時候,我們會選擇強制刪除。例如:

[root@localhost ~]# mkdir -p /test/lm/movie/jp
#重新建立測試目錄
[root@localhost ~]# rm -rf /test
#強制刪除,一了百了

加入了強制功能之後,刪除就會變得很簡單,但是需要注意,資料強制刪除之後無法恢復,除非依賴第三方的資料恢復工具,如 extundelete 等。但要注意,資料恢復很難恢復完整的資料,一般能恢復 70%~80% 就很難得了。所以,與其把寶壓在資料恢復上,不如養成良好的操作習慣。

雖然 "-rf" 選項是用來刪除目錄的,但是刪除檔案也不會報錯。所以,為了使用方便,一般不論是刪除檔案還是刪除目錄,都會直接使用 "-rf" 選項。

移動檔案或改名 mv

mv 命令(move 的縮寫),既可以在不同的目錄之間移動檔案或目錄,也可以對檔案和目錄進行重新命名。該命令的基本格式如下:

[root@localhost ~]# mv 【選項】 原始檔 目標檔案

選項:

  • -f:強制覆蓋,如果目標檔案已經存在,則不詢問,直接強制覆蓋;
  • -i:互動移動,如果目標檔案已經存在,則詢問使用者是否覆蓋(預設選項);
  • -n:如果目標檔案已經存在,則不會覆蓋移動,而且不詢問使用者;
  • -v:顯示檔案或目錄的移動過程;
  • -u:若目標檔案已經存在,但兩者相比,原始檔更新,則會對目標檔案進行升級;

需要注意的是,同 rm 命令類似,mv 命令也是一個具有破壞性的命令,如果使用不當,很可能給系統帶來災難性的後果。

【例 1】移動檔案或目錄

[root@localhost ~]# mv cangls /tmp
#移動之後,原始檔會被刪除,類似剪下
[root@localhost ~]# mkdir movie
[root@localhost ~]# mv movie/ /tmp
#也可以移動目錄。和 rm、cp 不同的是,mv 移動目錄不需要加入 "-r" 選項

如果移動的目標位置已經存在同名的檔案,則同樣會提示是否覆蓋,因為 mv 命令預設執行的也是 "mv -i" 的別名,例如:

[root@localhost ~]# touch cangls
#重新建立檔案
[root@localhost ~]# mv cangls /tmp
mv:縣否覆蓋"tmp/cangls"?y
#由於 /tmp 目錄下已經存在 cangls 檔案,所以會提示是否覆蓋,需要手工輸入 y 覆蓋移動

【例 2】強制移動
之前說過,如果目標目錄下已經存在同名檔案,則會提示是否覆蓋,需要手工確認。這時如果移動的同名檔案較多,則需要一個一個檔案進行確認,很不方便。

如果我們確認需要覆蓋已經存在的同名檔案,則可以使用 "-f" 選項進行強制移動,這就不再需要使用者手工確認了。例如:

[root@localhost ~]# touch cangls
#重新建立檔案
[root@localhost ~]# mv -f cangls /tmp
#就算 /tmp/ 目錄下已經存在同名的檔案,由於"-f"選項的作用,所以會強制覆蓋

【例 3】不覆蓋移動
既然可以強制覆蓋移動,那也有可能需要不覆蓋的移動。如果需要移動幾百個同名檔案,但是不想覆蓋,這時就需要 "-n" 選項的幫助了。例如:

[root@localhost ~]# ls /tmp
/tmp/bols /tmp/cangls
#在/tmp/目錄下已經存在bols、cangls檔案了
[root@localhost ~]# mv -vn bols cangls lmls /tmp/、
"lmls"->"/tmp/lmls"
#再向 /tmp/ 目錄中移動同名檔案,如果使用了 "-n" 選項,則可以看到只移動了 lmls,而同名的 bols 和 cangls 並沒有移動("-v" 選項用於顯示移動過程)

【例 4】改名
如果原始檔和目標檔案在同一目錄中,那就是改名。例如:

[root@localhost ~]# mv bols lmls
#把 bols 改名為 lmls

目錄也可以按照同樣的方法改名。

【例 5】顯示移動過程
如果我們想要知道在移動過程中到底有哪些檔案進行了移動,則可以使用 "-v" 選項來檢視詳細的移動資訊。例如:

[root@localhost ~]# touch test1.txt test2.txt test3.txt
#建立三個測試檔案
[root@localhost ~]# mv -v *.txt /tmp
"test1.txt" -> "/tmp/test1.txt"
"test2.txt" -> "/tmp/test2.txt"
"test3.txt" -> "/tmp/test3.txt"
#加入"-v"選項,可以看到有哪些檔案進行了移動

二、Linux 連結概念 ln

如果要想說清楚 ln 命令,則必須先解釋下 ext 檔案系統(Linux 檔案系統)是如何工作的。我們在前面講解了分割槽的格式化就是寫入檔案系統,而我們的 Linux 目前使用的是 ext4 檔案系統。如果用一張示意圖來描述 ext4 檔案系統,則可以參考圖。

ext4 檔案系統會把分割槽主要分為兩大部分(暫時不提超級塊):小部分用於儲存檔案的 inode (i 節點)資訊;剩餘的大部分用於儲存 block 資訊。

inode 的預設大小為 128 Byte,用來記錄檔案的許可權(r、w、x)、檔案的所有者和屬組、檔案的大小、檔案的狀態改變時間(ctime)、檔案的最近一次讀取時間(atime)、檔案的最近一次修改時間(mtime)、檔案的資料真正儲存的 block 編號。每個檔案需要佔用一個 inode。大家如果仔細檢視,就會發現 inode 中是不記錄檔名的,那是因為檔名記錄在檔案所在目錄的 block 中。

block 的大小可以是 1KB、2KB、4KB,預設為 4KB。block 用於實際的資料儲存,如果一個 block 放不下資料,則可以佔用多個 block。例如,有一個 10KB 的檔案需要儲存,則會佔用 3 個 block,雖然最後一個 block 不能佔滿,但也不能再放入其他檔案的資料。這 3 個 block 有可能是連續的,也有可能是分散的。

由此,我們可以知道以下 2 個重要的資訊:

  1. 每個檔案都獨自佔用一個 inode,檔案內容由 inode 的記錄來指向;
  2. 如果想要讀取檔案內容,就必須藉助目錄中記錄的檔名找到該檔案的 inode,才能成功找到檔案內容所在的 block 塊;

瞭解了 Linux 系統底層檔案的儲存狀態後,接下來學習 ln 命令。

ln 命令用於給檔案建立連結,根據 Linux 系統儲存檔案的特點,連結的方式分為以下 2 種:

  • 軟連結:類似於 Windows 系統中給檔案建立快捷方式,即產生一個特殊的檔案,該檔案用來指向另一個檔案,此連結方式同樣適用於目錄。
  • 硬連結:我們知道,檔案的基本資訊都儲存在 inode 中,而硬連結指的就是給一個檔案的 inode 分配多個檔名,通過任何一個檔名,都可以找到此檔案的 inode,從而讀取該檔案的資料資訊。

ln 命令的基本格式如下:

[root@localhost ~]# ln [選項] 原始檔 目標檔案

選項:

  • -s:建立軟連結檔案。如果不加 "-s" 選項,則建立硬連結檔案;
  • -f:強制。如果目標檔案已經存在,則刪除目標檔案後再建立連結檔案;

【例 1】建立硬連結:

[root@localhost ~]# touch cangls
[root@localhost ~]# ln /root/cangls /tmp
#建立硬連結檔案,目標檔案沒有寫檔名,會和原名一致
#也就是/tmp/cangls 是硬連結檔案

【例 2】建立軟連結:

[root@localhost ~]# touch bols
[root@localhost ~]# In -s /root/bols /tmp
#建立軟連結檔案

這裡需要注意的是,軟連結檔案的原始檔必須寫成絕對路徑,而不能寫成相對路徑(硬連結沒有這樣的要求);否則軟連結檔案會報錯。這是初學者非常容易犯的錯誤。

三、Linux 檔案內容檢視

Linux系統中使用以下命令來檢視檔案的內容:

  • cat 由第一行開始顯示檔案內容
  • tac 從最後一行開始顯示,可以看出 tac 是 cat 的倒著寫!
  • more 一頁一頁的顯示檔案內容
  • head 只看頭幾行
  • less 與 more 類似,但是比 more 更好的是,他可以往前翻頁!
  • tail 只看尾巴幾行

可以使用 man [命令] 或者 help [命令] 來檢視各個命令的使用文件,如 :man cphelp cp

連線檔案並列印輸出到標準輸出裝置 cat

cat 命令可以用來顯示文字檔案的內容(類似於 DOS 下的 type 命令),也可以把幾個檔案內容附加到另一個檔案中,即連線合並檔案。

關於此命令,有人認為寫 cat 命令的人是因為喜歡貓,因此給此命令起名為“cat”,其實不然,cat 是 concatenate(連線、連續)的簡寫。

cat 命令的基本格式如下:

[root@localhost ~]# cat [選項] 檔名
或者
[root@localhost ~]# cat 檔案1 檔案2 > 檔案3

這兩種格式中,前者用於顯示檔案的內容,常用選項及各自的含義如表 所示;而後者用於連線合並檔案。

選項 含義
-A 相當於 -vET 選項的整合,用於列出所有隱藏符號;
-E 列出每行結尾的回車符 $;
-n 對輸出的所有行進行編號;
-b 同 -n 不同,此選項表示只對非空行進行編號。
-T 把 Tab 鍵 ^I 顯示出來;
-V 列出特殊字元;
-s 當遇到有連續 2 行以上的空白行時,就替換為 1 行的空白行。

注意,cat 命令用於檢視檔案內容時,不論檔案內容有多少,都會一次性顯示。如果檔案非常大,那麼檔案開頭的內容就看不到了。不過 Linux 可以使用PgUp+上箭頭組合鍵向上翻頁,但是這種翻頁是有極限的,如果檔案足夠長,那麼還是無法看全檔案的內容。

因此,cat 命令適合檢視不太大的檔案。當然,在 Linux 中是可以使用其他的命令或方法來檢視大檔案的。

【例 1】cat 命令本身非常簡單,我們可以直接檢視檔案的內容。例如:

[root@localhost ~]# cat anaconda-ks.cfg
# Kickstart file automatically generated by anaconda.


#version=DEVEL
install
cdrom
lang zh一CN.UTF-8
…省略部分內容...

而如果使用 "-n" 選項,則會顯示行號。例如:

[root@localhost ~]# cat -n anaconda-ks.cfg
1 # Kickstart file automatically generated by anaconda.
2
3
4 #version=DEVEL
5 install
6 cdrom
…省略部分內容...

如果使用 "-A" 選項,則相當於使用了 "-vET" 選項,可以檢視文字中的所有隱藏符號,包括回車符($)、Tab 鍵(^I)等。例如:

[root@localhost ~]# cat -A anaconda-ks.cfg
# Kickstart file automatically generated by anaconda.$
$
$
#version=DEVEL$
install$
cdrom$
…省略部分內容…

【例 2】將檔案 file1.txt file2.txt 的內容合併後輸出到檔案 file3.txt 中。

[root@localhost base]# ls
file1.txt  file2.txt
[root@localhost base]# cat file1.txt
http://c.biancheng.net(file1.txt)
[root@localhost base]# cat file2.txt
is great(file2.txt)
[root@localhost base]# cat file1.txt file2.txt > file3.txt
[root@localhost base]# more file3.txt
#more 命令可檢視檔案中的內容
http://c.biancheng.net(file1.txt)
is great(file2.txt)
[root@localhost base]# ls
file1.txt  file2.txt  file3.txt

tac

tac與cat命令剛好相反,檔案內容從最後一行開始顯示,可以看出 tac 是 cat 的倒著寫。tac命令用於將檔案已行為單位的反序輸出,即第一行最後顯示,最後一行先顯示。

tac 命令的基本格式如下:

tac [選項] [引數]

選項:

-a或——append:將內容追加到檔案的末尾;
-i或——ignore-interrupts:忽略中斷訊號。

【例 1】

[root@hezhiqiang ~]# tac /etc/issue
Kernel \r on an \m
CentOS release 6.4 (Final)

分屏顯示檔案內容 more

在講解 cat 命令時,我們留下了一個疑問,即當使用 cat 命令檢視檔案內容時,如果檔案過大,以至使用PgUp+上箭頭組合鍵向上翻頁也無法看全檔案中的內容,該怎麼辦呢?這就需要使用 more 命令。

more 命令可以分頁顯示文字檔案的內容,使用者可以逐頁閱讀檔案中內容,此命令的基本格式如下:

[root@localhost ~]# more [選項] 檔名

more 命令比較簡單,一般不用什麼選項,對於表 中所列的選項,讀者只需看到認識即可。

選項 含義
-f 計算行數時,以實際的行數,而不是自動換行過後的行數。
-p 不以捲動的方式顯示每一頁,而是先清除螢幕後再顯示內容。
-c 跟 -p 選項相似,不同的是先顯示內容再清除其他舊資料。
-s 當遇到有連續兩行以上的空白行時,就替換為一行的空白行。
-u 不顯示下引號(根據環境變數 TERM 指定的終端而有所不同)。
+n 從第 n 行開始顯示檔案內容,n 代表數字。
-n 一次顯示的行數,n 代表數字。

more 命令的執行會開啟一個互動介面,因此讀者有必要了解一些互動命令,常用的互動命令如表所示。

互動指令 功能
h 或 ? 顯示 more 命令互動命令幫助。
q 或 Q 退出 more。
v 在當前行啟動一個編輯器。
:f 顯示當前檔案的檔名和行號。
!<命令> 或 :!<命令> 在子Shell中執行指定命令。
Enter鍵 向下移動一行。
空格鍵 向下移動一頁。
Ctrl+l 重新整理螢幕。
= 顯示當前行的行號。
' 轉到上一次搜尋開始的地方。
Ctrf+f 向下滾動一頁。
. 重複上次輸入的命令。
/ 字串 搜尋指定的字串。
d 向下移動半頁。
b 向上移動一頁。

【例 1】用分頁的方式顯示 anaconda-ks.cfg 檔案的內容。

[root@localhost ~]# more anaconda-ks.cfg
# Kickstart file automatically generated by anaconda.
#version=DEVEL
install
cdrom
…省略部分內容…
--More--(69%)
#在這裡執行互動命令即可

【例 2】用cat 配合管道符 |使用

[root@localhost ~]# cat anaconda-ks.cfg | more
# Kickstart file automatically generated by anaconda.
#version=DEVEL
install
cdrom
…省略部分內容…
--More--(69%)
#在這裡執行互動命令即可

顯示檔案開頭的內容 head

head 命令可以顯示指定檔案前若干行的檔案內容,其基本格式如下:

[root@localhost ~]# head [選項] 檔名

該命令常用選項以及各自的含義,如表 所示。

選項 含義
-n K 這裡的 K 表示行數,該選項用來顯示檔案前 K 行的內容;如果使用 "-K" 作為引數,則表示除了檔案最後 K 行外,顯示剩餘的全部內容。
-c K 這裡的 K 表示位元組數,該選項用來顯示檔案前 K 個位元組的內容;如果使用 "-K",則表示除了檔案最後 K 位元組的內容,顯示剩餘全部內容。
-v 顯示檔名;

注意,如不設定顯示的具體行數,則預設顯示 10 行的文字資料。

【例 1】基本用法。

[root@localhost ~]# head anaconda-ks.cfg

head 命令預設顯示檔案的開頭 10 行內容。如果想顯示指定的行數,則只需使用 "-n" 選項即可,例如:

[root@localhost ~]# head -n 20 anaconda-ks.cfg

這是顯示檔案的開頭 20 行內容,也可以直接寫 "-行數",例如:

[root@localhost ~]# head -20 anaconda-ks.cfg

檢視檔案內容 less

less 命令的作用和 more 十分類似,都用來瀏覽文字檔案中的內容,不同之處在於,使用 more 命令瀏覽檔案內容時,只能不斷向後翻看,而使用 less 命令瀏覽,既可以向後翻看,也可以向前翻看。

不僅如此,為了方面使用者瀏覽文字內容,less 命令還提供了以下幾個功能:

  • 使用游標鍵可以在文字檔案中前後(左後)滾屏;
  • 用行號或百分比作為書籤瀏覽檔案;
  • 提供更加友好的檢索、高亮顯示等操作;
  • 相容常用的字處理程式(如 Vim、Emacs)的鍵盤操作;
  • 閱讀到檔案結束時,less 命令不會退出;
  • 螢幕底部的資訊提示更容易控制使用,而且提供了更多的資訊。

less 命令的基本格式如下:

[root@localhost ~]# less [選項] 檔名

此命令可用的選項以及各自的含義如表 所示。

選項 選項含義
-N 顯示每行的行號。
-S 行過長時將超出部分捨棄。
-e 當檔案顯示結束後,自動離開。
-g 只標誌最後搜尋到的關鍵同。
-Q 不使用警告音。
-i 忽略搜尋時的大小寫。
-m 顯示類似 more 命令的百分比。
-f 強迫開啟特殊檔案,比如外圍裝置代號、目錄和二進位制檔案。
-s 顯示連續空行為一行。
-b <緩衝區大小> 設定緩衝區的大小。
-o <檔名> 將 less 輸出的內容儲存到指定檔案中。
-x <數字> 將【Tab】鍵顯示為規定的數字空格。

在使用 less 命令檢視檔案內容的過程中,和 more 命令一樣,也會進入互動介面,因此需要讀者掌握一些常用的互動指令,如表 所示。

互動指令 功能
/字串 向下搜尋“字串”的功能。
?字串 向上搜尋“字串”的功能。
n 重複*前一個搜尋(與 / 成 ? 有關)。
N 反向重複前一個搜尋(與 / 或 ? 有關)。
b 向上移動一頁。
d 向下移動半頁。
h 或 H 顯示幫助介面。
q 或 Q 退出 less 命令。
y 向上移動一行。
空格鍵 向下移動一頁。
Enter鍵 向下移動一行。
【PgDn】鍵 向下移動一頁。
【PgUp】鍵 向上移動一頁。
Ctrl+f 向下移動一頁。
Ctrl+b 向上移動一頁。
Ctrl+d 向下移動一頁。
Ctrl+u 向上移動半頁。
j 向下移動一行。
k 向上移動一行。
G 移動至最後一行。
g 移動到第一行。
ZZ 退出 less 命令。
v 使用配置的編輯器編輯當前檔案。
[ 移動到本文件的上一個節點。
] 移動到本文件的下一個節點。
p 移動到同級的上一個節點。
u 向上移動半頁。

【例 1】使用 less 命令檢視 /boot/grub/grub.cfg 檔案中的內容。

[root@localhost ~]# less /boot/grub/grub.cfg
#
#DO NOT EDIT THIS FILE
#
#It is automatically generated by grub-mkconfig using templates from /etc/grub.d and settings from /etc/default/grub
#

### BEGIN /etc/grub.d/00_header ###
if [ -s $prefix/grubenv ]; then
 set have_grubenv=true
 load_env
fi
set default="0"
if [ "$ {prev_saved_entry}" ]; then
 set saved_entry="${prev_saved_entry}"
 save_env saved_entry
 set prev_saved_entry= save_env prev_saved_entry
 set boot_once=true
fi

function savedefault {
 if [ -z "${boot_once}" ]; then
:

可以看到,less 在螢幕底部顯示一個冒號(:),等待使用者輸入命令,比如說,使用者想向下翻一頁,可以按空格鍵;如果想向上翻一頁,可以按 b 鍵。

顯示檔案結尾的內容 tail

tail 命令和 head 命令正好相反,它用來檢視檔案末尾的資料,其基本格式如下:

[root@localhost ~]# tail [選項] 檔名

此命令常用的選項及含義如表 所示。

選項 含義
-n K 這裡的 K 指的是行數,該選項表示輸出最後 K 行,在此基礎上,如果使用 -n +K,則表示從檔案的第 K 行開始輸出。
-c K 這裡的 K 指的是位元組數,該選項表示輸出檔案最後 K 個位元組的內容,在此基礎上,使用 -c +K 則表示從檔案第 K 個位元組開始輸出。
-f 輸出檔案變化後新增加的資料。

【例 1】檢視 /etc/passwd 檔案最後 3 行的資料內容。

[root@localhost ~]# tail -n 3 /etc/passwd
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
oprofile:x:16:16:Special user account to be used by OProfile:/var/lib/oprofile:/sbin/nologin
tcpdump:x:72:72::/:/sbin/nologin

除此之外,還可以採用如下這種形式:

[root@localhost ~]# tail -3 /etc/passwd
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
oprofile:x:16:16:Special user account to be used by OProfile:/var/lib/oprofile:/sbin/nologin
tcpdump:x:72:72::/:/sbin/nologin

可以看到,使用 tail -n 3 /etc/passwd 命令和 tail -3 /etc/passwd 的效果是一樣的。

【例 2】檢視 /etc/passwd 檔案末尾 100 個位元組的資料內容。

[root@localhost ~]# tail -c 100 /etc/passwd
cpdump:x:72:72::/:/sbin/nologin

【例 3】 監聽檔案的新増內容。tail 命令有一種比較有趣的用法,可以使用 "-f" 選項來監聽檔案的新増內容,例如:

[root@localhost ~]#tail -f anaconda-ks.cfg
@server-platform
@server-policy
pax
oddjob
sgpio
certmonger
pam_krb5
krb5-workstation
perl-DBD-SQLite
%end
#游標不會退出檔案,而會一直監聽在檔案的結尾處

這條命令會顯示檔案的最後 10 行內容,而且游標不會退出命令,每隔一秒會檢查一下檔案是否增加新的內容,如果增加就追加到原來的輸出結果後面並顯示。因此,這時如果向檔案中追加一些資料(需要開啟一個新終端):

[root@localhost ~]# echo 2222222222 >> anaconda-ks.cfg
[root@localhost ~]# echo 3333333333 >> anaconda-ks.cfg
#在新終端中通過echo命令向檔案中追加資料

那麼,在原始的正在監聽的終端中,會看到如下資訊:

[root@localhost ~]# tail -f anaconda-ks.cfg @server-platforin
@server-policy
pax
oddjob
sgpio
certmonger
pam_krb5
krb5-workstation
perl-DBD-SQLite
%end2222222222
33333333333
#在檔案的結尾處監聽到了新増資料

如果想終止輸出,按【Ctrl+c】鍵中斷 tail 命令即可。

四、Linux重定向(輸入輸出重定向)

我們知道,Linux 中標準的輸入裝置預設指的是鍵盤,標準的輸出裝置預設指的是顯示器。而本節所要介紹的輸入、輸出重定向,完全可以從字面意思去理解,也就是:

  • 輸入重定向:指的是重新指定裝置來代替鍵盤作為新的輸入裝置;
  • 輸出重定向:指的是重新指定裝置來代替顯示器作為新的輸出裝置。

通常是用檔案或命令的執行結果來代替鍵盤作為新的輸入裝置,而新的輸出裝置通常指的就是檔案。

Linux輸入重定向

對於輸入重定向來說,其需要用到的符號以及作用如表 所示。

命令符號格式 作用
命令 < 檔案 將指定檔案作為命令的輸入裝置
命令 << 分界符 表示從標準輸入裝置(鍵盤)中讀入,直到遇到分界符才停止(讀入的資料不包括分界符),這裡的分界符其實就是自定義的字串
命令 < 檔案 1 > 檔案 2 將檔案 1 作為命令的輸入裝置,該命令的執行結果輸出到檔案 2 中。

【例 1】
預設情況下,cat 命令會接受標準輸入裝置(鍵盤)的輸入,並顯示到控制檯,但如果用檔案代替鍵盤作為輸入裝置,那麼該命令會以指定的檔案作為輸入裝置,並將檔案中的內容讀取並顯示到控制檯。

/etc/passwd 檔案(儲存了系統中所有使用者的基本資訊)為例,執行如下命令:

[root@localhost ~]# cat /etc/passwd
#這裡省略輸出資訊,讀者可自行檢視
[root@localhost ~]# cat < /etc/passwd
#輸出結果同上面命令相同

注意,雖然執行結果相同,但第一行代表是以鍵盤作為輸入裝置,而第二行程式碼是以 /etc/passwd 檔案作為輸入裝置。

【例 2】

[root@localhost ~]# cat << 0
>c.biancheng.net
>Linux
>0
c.biancheng.net
Linux

可以看到,當指定了 0 作為分界符之後,只要不輸入 0,就可以一直輸入資料。

【例 3】
首先,新建文字檔案 a.tx,然後執行如下命令:

[root@localhost ~]# cat a.txt
[root@localhost ~]# cat < /etc/passwd > a.txt
[root@localhost ~]# cat a.txt
#輸出了和 /etc/passwd 檔案內容相同的資料

可以看到,通過重定向 /etc/passwd 作為輸入裝置,並輸出重定向到 a.txt,最終實現了將 /etc/passwd 檔案中內容複製到a.txt中。

Linux輸出重定向

相較於輸入重定向,我們使用輸出重定向的頻率更高。並且,和輸入重定向不同的是,輸出重定向還可以細分為標準輸出重定向和錯誤輸出重定向兩種技術。

例如,使用 ls 命令分別檢視兩個檔案的屬性資訊,但其中一個檔案是不存在的,如下所示:

[root@localhost ~]# touch demo1.txt
[root@localhost ~]# ls -l demo1.txt
-rw-rw-r--. 1 root root 0 Oct 12 15:02 demo1.txt
[root@localhost ~]# ls -l demo2.txt  <-- 不存在的檔案
ls: cannot access demo2.txt: No such file or directory

上述命令中,demo1.txt 是存在的,因此正確輸出了該檔案的一些屬性資訊,這也是該命令執行的標準輸出資訊;而 demo2.txt 是不存在的,因此執行 ls 命令之後顯示的報錯資訊,是該命令的錯誤輸出資訊。

再次強調,要想把原本輸出到螢幕上的資料轉而寫入到檔案中,這兩種輸出資訊就要區別對待。

在此基礎上,標準輸出重定向和錯誤輸出重定向又分別包含清空寫入和追加寫入兩種模式。因此,對於輸出重定向來說,其需要用到的符號以及作用如表 所示。

命令符號格式 作用
命令 > 檔案 將命令執行的標準輸出結果重定向輸出到指定的檔案中,如果該檔案已包含資料,會清空原有資料,再寫入新資料。
命令 2> 檔案 將命令執行的錯誤輸出結果重定向到指定的檔案中,如果該檔案中已包含資料,會清空原有資料,再寫入新資料。
命令 >> 檔案 將命令執行的標準輸出結果重定向輸出到指定的檔案中,如果該檔案已包含資料,新資料將寫入到原有內容的後面。
命令 2>> 檔案 將命令執行的錯誤輸出結果重定向到指定的檔案中,如果該檔案中已包含資料,新資料將寫入到原有內容的後面。
命令 >> 檔案 2>&1 或者 命令 &>> 檔案 將標準輸出或者錯誤輸出寫入到指定檔案,如果該檔案中已包含資料,新資料將寫入到原有內容的後面。注意,第一種格式中,最後的 2>&1 是一體的,可以認為是固定寫法。

【例 4】新建一個包含有 "Linux" 字串的文字檔案 Linux.txt,以及空文字檔案 demo.txt,然後執行如下命令:

[root@localhost ~]# cat Linux.txt > demo.txt
[root@localhost ~]# cat demo.txt
Linux
[root@localhost ~]# cat Linux.txt > demo.txt
[root@localhost ~]# cat demo.txt
Linux   <--這裡的 Linux 是清空原有的 Linux 之後,寫入的新的 Linux
[root@localhost ~]# cat Linux.txt >> demo.txt
[root@localhost ~]# cat demo.txt
Linux
Linux   <--以追加的方式,新資料寫入到原有資料之後
[root@localhost ~]# cat b.txt > demo.txt
cat: b.txt: No such file or directory <-- 錯誤輸出資訊依然輸出到了顯示器中
[root@localhost ~]# cat b.txt 2> demo.txt
[root@localhost ~]# cat demo.txt
cat: b.txt: No such file or directory <--清空檔案,再將錯誤輸出資訊寫入到該檔案中
[root@localhost ~]# cat b.txt 2>> demo.txt
[root@localhost ~]# cat demo.txt
cat: b.txt: No such file or directory
cat: b.txt: No such file or directory <--追加寫入錯誤輸出資訊

五、文字處理 (Linux三劍客):grep、sed、awk

查詢檔案內容 grep

很多時候,我們並不需要列出檔案的全部內容,而是從檔案中找到包含指定資訊的那些行,要實現這個目的,可以使用 grep 命令。

grep 命令的由來可以追溯到 UNIX 誕生的早期,在 UNIX 系統中,搜尋的模式(patterns)被稱為正規表示式(regular expressions),為了要徹底搜尋一個檔案,有的使用者在要搜尋的字串前加上字首 global(全面的),一旦找到相匹配的內容,使用者就像將其輸出(print)到螢幕上,而將這一系列的操作整合到一起就是 global regular expressions print,而這也就是 grep 命令的全稱。

grep命令能夠在一個或多個檔案中,搜尋某一特定的字元模式(也就是正規表示式),此模式可以是單一的字元、字串、單詞或句子。

正規表示式是描述一組字串的一個模式,正規表示式的構成模仿了數學表示式,通過使用操作符將較小的表示式組合成一個新的表示式。正規表示式可以是一些純文字文字,也可以是用來產生模式的一些特殊字元。為了進一步定義一個搜尋模式,grep 命令支援如表 所示的這幾種正規表示式的元字元(也就是萬用字元)。

萬用字元 功能
c* 將匹配 0 個(即空白)或多個字元 c(c 為任一字元)。
. 將匹配任何一個字元,且只能是一個字元。
[xyz] 匹配方括號中的任意一個字元。
[^xyz] 匹配除方括號中字元外的所有字元。
^ 鎖定行的開頭。
$ 鎖定行的結尾。

需要注意的是,在基本正規表示式中,如萬用字元 *、+、{、|、( 和 )等,已經失去了它們原本的含義,而若要恢復它們原本的含義,則要在之前新增反斜槓 \,如 *、+、{、|、( 和 )。

grep 命令是用來在每一個檔案或中(或特定輸出上)搜尋特定的模式,當使用 grep 時,包含指定字元模式的每一行內容,都會被列印(顯示)到螢幕上,但是使用 grep 命令並不改變檔案中的內容。

grep 命令的基本格式如下:

[root@localhost ~]# grep [選項] 模式 檔名

這裡的模式,要麼是字元(串),要麼是正規表示式。而此命令常用的選項以及各自的含義如表 所示。

選項 含義
-c 僅列出檔案中包含模式的行數。
-i 忽略模式中的字母大小寫。
-l 列出帶有匹配行的檔名。
-n 在每一行的最前面列出行號。
-v 列出沒有匹配模式的行。
-w 把表示式當做一個完整的單字元來搜尋,忽略那些部分匹配的行。

注意,如果是搜尋多個檔案,grep 命令的搜尋結果只顯示檔案中發現匹配模式的檔名;而如果搜尋單個檔案,grep 命令的結果將顯示每一個包含匹配模式的行。

【例 1】假設有一份 emp.data 員工清單,現在要搜尋此檔案,找出職位為 CLERK 的所有員工,則執行命令如下:

[root@localhost ~]# grep CLERK emp.data
#忽略輸出內容

而在此基礎上,如果只想知道職位為 CLERK 的員工的人數,可以使用“-c”選項,執行命令如下:

[root@localhost ~]# grep -c CLERK emp.data
#忽略輸出內容

【例 2】搜尋 emp.data 檔案,使用正規表示式找出以 78 開頭的資料行,執行命令如下:

[root@localhost ~]# grep ^78 emp.data
#忽略輸出內容

grep 命令的功能非常強大,通過利用它的不同選項以及變化萬千的正規表示式,可以獲取任何我們所需要的資訊。本節所介紹的 grep 命令,只介紹了它的一部分基礎知識,比如說,grep 命令可用的選項還有很多,且用法也五花八門,不過到這就夠用了。

sed

我們知道,Vim 採用的是互動式文字編輯模式,你可以用鍵盤命令來互動性地插入、刪除或替換資料中的文字。但本節要講的 sed 命令不同,它採用的是流編輯模式,最明顯的特點是,在 sed 處理資料之前,需要預先提供一組規則,sed 會按照此規則來編輯資料。

sed 會根據指令碼命令來處理文字檔案中的資料,這些命令要麼從命令列中輸入,要麼儲存在一個文字檔案中,此命令執行資料的順序如下:

  1. 每次僅讀取一行內容;
  2. 根據提供的規則命令匹配並修改資料。注意,sed 預設不會直接修改原始檔資料,而是會將資料複製到緩衝區中,修改也僅限於緩衝區中的資料;
  3. 將執行結果輸出。

當一行資料匹配完成後,它會繼續讀取下一行資料,並重復這個過程,直到將檔案中所有資料處理完畢。

sed 命令的基本格式如下:

[root@localhost ~]# sed [選項] [指令碼命令] 檔名

該命令常用的選項及含義,如表 1 所示。

選項 含義
-e 指令碼命令 該選項會將其後跟的指令碼命令新增到已有的命令中。
-f 指令碼命令檔案 該選項會將其後檔案中的指令碼命令新增到已有的命令中。
-n 預設情況下,sed 會在所有的指令碼指定執行完畢後,會自動輸出處理後的內容,而該選項會遮蔽啟動輸出,需使用 print 命令來完成輸出。
-i 此選項會直接修改原始檔,要慎用。

成功使用 sed 命令的關鍵在於掌握各式各樣的指令碼命令及格式,它能幫你定製編輯檔案的規則。

sed指令碼命令

sed s 替換指令碼命令

此命令的基本格式為:

[address]s/pattern/replacement/flags

其中,address 表示指定要操作的具體行,pattern 指的是需要替換的內容,replacement 指的是要替換的新內容。

關於指定具體操作行(address)的用法,這裡先不做解釋,文章後續會對其做詳細介紹。

此命令中常用的 flags 標記如表 2 所示。

flags 標記 功能
n 1~512 之間的數字,表示指定要替換的字串出現第幾次時才進行替換,例如,一行中有 3 個 A,但使用者只想替換第二個 A,這是就用到這個標記;
g 對資料中所有匹配到的內容進行替換,如果沒有 g,則只會在第一次匹配成功時做替換操作。例如,一行資料中有 3 個 A,則只會替換第一個 A;
p 會列印與替換命令中指定的模式匹配的行。此標記通常與 -n 選項一起使用。
w file 將緩衝區中的內容寫到指定的 file 檔案中;
& 用正規表示式匹配的內容進行替換;
\n 匹配第 n 個子串,該子串之前在 pattern 中用 () 指定。
\ 轉義(轉義替換部分包含:&、\ 等)。

比如,可以指定 sed 用新文字替換第幾處模式匹配的地方:

[root@localhost ~]# sed 's/test/trial/2' data4.txt
This is a test of the trial script.
This is the second test of the trial script.

可以看到,使用數字 2 作為標記的結果就是,sed 編輯器只替換每行中第 2 次出現的匹配模式。

如果要用新檔案替換所有匹配的字串,可以使用 g 標記:

[root@localhost ~]# sed 's/test/trial/g' data4.txt
This is a trial of the trial script.
This is the second trial of the trial script.

我們知道,-n 選項會禁止 sed 輸出,但 p 標記會輸出修改過的行,將二者匹配使用的效果就是隻輸出被替換命令修改過的行,例如:

[root@localhost ~]# cat data5.txt
This is a test line.
This is a different line.
[root@localhost ~]# sed -n 's/test/trial/p' data5.txt
This is a trial line.

w 標記會將匹配後的結果儲存到指定檔案中,比如:

[root@localhost ~]# sed 's/test/trial/w test.txt' data5.txt
This is a trial line.
This is a different line.
[root@localhost ~]#cat test.txt
This is a trial line.

在使用 s 指令碼命令時,替換類似檔案路徑的字串會比較麻煩,需要將路徑中的正斜線進行轉義,例如:

[root@localhost ~]# sed 's/\/bin\/bash/\/bin\/csh/' /etc/passwd
sed d 替換指令碼命令

此命令的基本格式為:

[address]d

如果需要刪除文字中的特定行,可以用 d 指令碼命令,它會刪除指定行中的所有內容。但使用該命令時要特別小心,如果你忘記指定具體行的話,檔案中的所有內容都會被刪除,舉個例子:

[root@localhost ~]# cat data1.txtThe quick brown fox jumps over the lazy dogThe quick brown fox jumps over the lazy dogThe quick brown fox jumps over the lazy dogThe quick brown fox jumps over the lazy dog[root@localhost ~]# sed 'd' data1.txt#什麼也不輸出,證明成了空檔案

當和指定地址一起使用時,刪除命令顯然能發揮出大的功用。可以從資料流中刪除特定的文字行。

address 的具體寫法後續會做詳細介紹,這裡只給大家舉幾個簡單的例子:

  • 通過行號指定,比如刪除 data6.txt 檔案內容中的第 3 行:

    [root@localhost ~]# cat data6.txt
    This is line number 1.
    This is line number 2.
    This is line number 3.
    This is line number 4.
    [root@localhost ~]# sed '3d' data6.txt
    This is line number 1.
    This is line number 2.
    This is line number 4.
    
  • 或者通過特定行區間指定,比如刪除 data6.txt 檔案內容中的第 2、3行:

    [root@localhost ~]# sed '2,3d' data6.txt
    This is line number 1.
    This is line number 4.
    
  • 也可以使用兩個文字模式來刪除某個區間內的行,但這麼做時要小心,你指定的第一個模式會“開啟”行刪除功能,第二個模式會“關閉”行刪除功能,因此,sed 會刪除兩個指定行之間的所有行(包括指定的行),例如:

    [root@localhost ~]#sed '/1/,/3/d' data6.txt
    #刪除第 1~3 行的文字資料
    This is line number 4.
    
  • 或者通過特殊的檔案結尾字元,比如刪除 data6.txt 檔案內容中第 3 行開始的所有的內容:

    [root@localhost ~]# sed '3,$d' data6.txt
    This is line number 1.
    This is line number 2.
    

在此強調,在預設情況下 sed 並不會修改原始檔案,這裡被刪除的行只是從 sed 的輸出中消失了,原始檔案沒做任何改變。

sed a 和 i 指令碼命令

a 命令表示在指定行的後面附加一行,i 命令表示在指定行的前面插入一行,這裡之所以要同時介紹這 2 個指令碼命令,因為它們的基本格式完全相同,如下所示:

[address]a(或 i)\新文字內容

下面分別就這 2 個命令,給讀者舉幾個例子。比如說,將一個新行插入到資料流第三行前,執行命令如下:

[root@localhost ~]# sed '3i\
> This is an inserted line.' data6.txt
This is line number 1.
This is line number 2.
This is an inserted line.
This is line number 3.
This is line number 4.

再比如說,將一個新行附加到資料流中第三行後,執行命令如下:

[root@localhost ~]# sed '3a\
> This is an appended line.' data6.txt
This is line number 1.
This is line number 2.
This is line number 3.
This is an appended line.
This is line number 4.

如果你想將一個多行資料新增到資料流中,只需對要插入或附加的文字中的每一行末尾(除最後一行)新增反斜線即可,例如:

[root@localhost ~]# sed '1i\
> This is one line of new text.\
> This is another line of new text.' data6.txt
This is one line of new text.
This is another line of new text.
This is line number 1.
This is line number 2.
This is line number 3.
This is line number 4.

可以看到,指定的兩行都會被新增到資料流中。

sed c 替換指令碼命令

c 命令表示將指定行中的所有內容,替換成該選項後面的字串。該命令的基本格式為:

[address]c\用於替換的新文字

舉個例子:

[root@localhost ~]# sed '3c\
> This is a changed line of text.' data6.txt
This is line number 1.
This is line number 2.
This is a changed line of text.
This is line number 4.

在這個例子中,sed 編輯器會修改第三行中的文字,其實,下面的寫法也可以實現此目的:

[root@localhost ~]# sed '/number 3/c\
> This is a changed line of text.' data6.txt
This is line number 1.
This is line number 2.
This is a changed line of text.
This is line number 4.
sed y 轉換指令碼命令

y 轉換命令是唯一可以處理單個字元的 sed 指令碼命令,其基本格式如下:

[address]y/inchars/outchars/

轉換命令會對 inchars 和 outchars 值進行一對一的對映,即 inchars 中的第一個字元會被轉換為 outchars 中的第一個字元,第二個字元會被轉換成 outchars 中的第二個字元...這個對映過程會一直持續到處理完指定字元。如果 inchars 和 outchars 的長度不同,則 sed 會產生一條錯誤訊息。

舉個簡單例子:

[root@localhost ~]# sed 'y/123/789/' data8.txt
This is line number 7.
This is line number 8.
This is line number 9.
This is line number 4.
This is line number 7 again.
This is yet another line.
This is the last line in the file.

可以看到,inchars 模式中指定字元的每個例項都會被替換成 outchars 模式中相同位置的那個字元。

轉換命令是一個全域性命令,也就是說,它會文字行中找到的所有指定字元自動進行轉換,而不會考慮它們出現的位置,再打個比方:

[root@localhost ~]# echo "This 1 is a test of 1 try." | sed 'y/123/456/'
This 4 is a test of 4 try.

sed 轉換了在文字行中匹配到的字元 1 的兩個例項,我們無法限定只轉換在特定地方出現的字元。

sed p 列印指令碼命令

p 命令表示搜尋符號條件的行,並輸出該行的內容,此命令的基本格式為:

[address]p

p 命令常見的用法是列印包含匹配文字模式的行,例如:

[root@localhost ~]# cat data6.txt
This is line number 1.
This is line number 2.
This is line number 3.
This is line number 4.
[root@localhost ~]# sed -n '/number 3/p' data6.txt
This is line number 3.

可以看到,用 -n 選項和 p 命令配合使用,我們可以禁止輸出其他行,只列印包含匹配文字模式的行。

如果需要在修改之前檢視行,也可以使用列印命令,比如與替換或修改命令一起使用。可以建立一個指令碼在修改行之前顯示該行,如下所示:

[root@localhost ~]# sed -n '/3/{
> p
> s/line/test/p
> }' data6.txt
This is line number 3.
This is test number 3.

sed 命令會查詢包含數字 3 的行,然後執行兩條命令。首先,指令碼用 p 命令來列印出原始行;然後它用 s 命令替換文字,並用 p 標記列印出替換結果。輸出同時顯示了原來的行文字和新的行文字。

sed w 指令碼命令

w 命令用來將文字中指定行的內容寫入檔案中,此命令的基本格式如下:

[address]w filename

這裡的 filename 表示檔名,可以使用相對路徑或絕對路徑,但不管是哪種,執行 sed 命令的使用者都必須有檔案的寫許可權。

下面的例子是將資料流中的前兩行列印到一個文字檔案中:

[root@localhost ~]# sed '1,2w test.txt' data6.txt
This is line number 1.
This is line number 2.
This is line number 3.
This is line number 4.
[root@localhost ~]# cat test.txt
This is line number 1.
This is line number 2.

當然,如果不想讓行直接輸出,可以用 -n 選項,再舉個例子:

[root@localhost ~]# cat data11.txt
Blum, R    Browncoat
McGuiness, A Alliance
Bresnahan, C Browncoat
Harken, C   Alliance
[root@localhost ~]# sed -n '/Browncoat/w Browncoats.txt' data11.txt
cat Browncoats.txt
Blum, R    Browncoat
Bresnahan, C Browncoat

可以看到,通過使用 w 指令碼命令,sed 可以實現將包含文字模式的資料行寫入目標檔案。

sed r 指令碼命令

r 命令用於將一個獨立檔案的資料插入到當前資料流的指定位置,該命令的基本格式為:

[address]r filename

sed 命令會將 filename 檔案中的內容插入到 address 指定行的後面,比如說:

[root@localhost ~]# cat data12.txt
This is an added line.
This is the second added line.
[root@localhost ~]# sed '3r data12.txt' data6.txt
This is line number 1.
This is line number 2.
This is line number 3.
This is an added line.
This is the second added line.
This is line number 4.

如果你想將指定檔案中的資料插入到資料流的末尾,可以使用 $ 地址符,例如:

[root@localhost ~]# sed '$r data12.txt' data6.txt
This is line number 1.
This is line number 2.
This is line number 3.
This is line number 4.
This is an added line.
This is the second added line.
sed q 退出指令碼命令

q 命令的作用是使 sed 命令在第一次匹配任務結束後,退出 sed 程式,不再進行對後續資料的處理。

比如:

[root@localhost ~]# sed '2q' test.txt
This is line number 1.
This is line number 2.

可以看到,sed 命令在列印輸出第 2 行之後,就停止了,是 q 命令造成的,再比如:

[root@localhost ~]# sed '/number 1/{ s/number 1/number 0/;q; }' test.txt
This is line number 0.

使用 q 命令之後,sed 命令會在匹配到 number 1 時,將其替換成 number 0,然後直接退出。

sed 指令碼命令的定址方式

前面在介紹各個指令碼命令時,我們一直忽略了對 address 部分的介紹。對各個指令碼命令來說,address 用來表明該指令碼命令作用到文字中的具體行。

預設情況下,sed 命令會作用於文字資料的所有行。如果只想將命令作用於特定行或某些行,則必須寫明 address 部分,表示的方法有以下 2 種:

  1. 以數字形式指定行區間;
  2. 用文字模式指定具體行區間。

以上兩種形式都可以使用如下這 2 種格式,分別是:

[address]指令碼命令

或者

address {
  多個指令碼命令
}

以上兩種形式在前面例子中都有具體例項,因此這裡不再做過多贅述。

以數字形式指定行區間

當使用數字方式的行定址時,可以用行在文字流中的行位置來引用。sed 會將文字流中的第一行編號為 1,然後繼續按順序為接下來的行分配行號。

在指令碼命令中,指定的地址可以是單個行號,或是用起始行號、逗號以及結尾行號指定的一定區間範圍內的行。這裡舉一個 sed 命令作用到指定行號的例子:

[root@localhost ~]#sed '2s/dog/cat/' data1.txt
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy cat
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy dog

可以看到,sed 只修改地址指定的第二行的文字。下面的例子中使用了行地址區間:

[root@localhost ~]# sed '2,3s/dog/cat/' data1.txt
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy cat
The quick brown fox jumps over the lazy cat
The quick brown fox jumps over the lazy dog

在此基礎上,如果想將命令作用到文字中從某行開始的所有行,可以用特殊地址——美元符($):

[root@localhost ~]# sed '2,$s/dog/cat/' data1.txt
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy cat
The quick brown fox jumps over the lazy cat
The quick brown fox jumps over the lazy cat
用文字模式指定行區間

sed 允許指定文字模式來過濾出命令要作用的行,格式如下:

/pattern/command

注意,必須用正斜線將要指定的 pattern 封起來,sed 會將該命令作用到包含指定文字模式的行上。

舉個例子,如果你想只修改使用者 demo 的預設 shell,可以使用 sed 命令,執行命令如下:

[root@localhost ~]# grep demo /etc/passwd
demo:x:502:502::/home/Samantha:/bin/bash
[root@localhost ~]# sed '/demo/s/bash/csh/' /etc/passwd
root:x:0:0:root:/root:/bin/bash
...
demo:x:502:502::/home/demo:/bin/csh
...

雖然使用固定文字模式能幫你過濾出特定的值,就跟上面這個使用者名稱的例子一樣,但其作用難免有限,因此,sed 允許在文字模式使用正規表示式指明作用的具體行。正規表示式允許建立高階文字模式匹配表示式來匹配各種資料。這些表示式結合了一系列萬用字元、特殊字元以及固定文字字元來生成能夠匹配幾乎任何形式文字的簡練模式。

關於正規表示式,本節不做過多介紹,有興趣的讀者可閱讀《正規表示式入門教程》一文,這裡僅給讀者提供一個簡單示例:

[root@localhost ~]# cat test.txt
<html>
<title>First Wed</title>
<body>
h1Helloh1
h2Helloh2
h3Helloh3
</body>
</html>
#使用正則表示式給所有第一個的h1、h2、h3新增<>,給第二個h1、h2、h3新增</>
[root@localhost ~]# cat sed.sh
/h[0-9]/{
    s//\<&\>/1
    s//\<\/&\>/2
}
[root@localhost ~]# sed -f sed.sh test.txt
<h1>Hello</h1>
<h2>Hello</h2>
<h3>Hello</h3>

awk

除了使用 sed 命令,Linux 系統中還有一個功能更加強大的文字資料處理工具,就是 awk。它誕生於 20 世紀 70 年代末期,這也許是它影響了眾多 Linux 使用者的原因之一。

曾有人推測 awk 命令的名字來源於 awkward 這個單詞。其實不然,此命令的設計者有 3 位,他們的姓分別是 Aho、Weingberger 和 Kernighan,awk 就取自這 3 為大師姓的首字母。

和 sed 命令類似,awk 命令也是逐行掃描檔案(從第 1 行到最後一行),尋找含有目標文字的行,如果匹配成功,則會在該行上執行使用者想要的操作;反之,則不對行做任何處理。

awk 命令的基本格式為:

[root@localhost ~]# awk [選項] '指令碼命令' 檔名

此命令常用的選項以及各自的含義,如表 所示。

選項 含義
-F fs 指定以 fs 作為輸入行的分隔符,awk 命令預設分隔符為空格或製表符。
-f file 從指令碼檔案中讀取 awk 指令碼指令,以取代直接在命令列中輸入指令。
-v var=val 在執行處理過程之前,設定一個變數 var,並給其裝置初始值為 val。

awk 的強大之處在於指令碼命令,它由 2 部分組成,分別為匹配規則和執行命令,如下所示:

'匹配規則{執行命令}'

這裡的匹配規則,和 sed 命令中的 address 部分作用相同,用來指定指令碼命令可以作用到文字內容中的具體行,可以使用字串(比如 /demo/,表示檢視含有 demo 字串的行)或者正規表示式指定。另外需要注意的是,整個指令碼命令是用單引號('')括起,而其中的執行命令部分需要用大括號({})括起來。

在 awk 程式執行時,如果沒有指定執行命令,則預設會把匹配的行輸出;如果不指定匹配規則,則預設匹配文字中所有的行。

舉個簡單的例子:

[root@localhost ~]# awk '/^$/ {print "Blank line"}' test.txt

在此命令中,/^$/ 是一個正規表示式,功能是匹配文字中的空白行,同時可以看到,執行命令使用的是 print 命令,此命令經常會使用,它的作用很簡單,就是將指定的文字進行輸出。因此,整個命令的功能是,如果 test.txt 有 N 個空白行,那麼執行此命令會輸出 N 個 Blank line。

awk 使用資料欄位變數

awk 的主要特性之一是其處理文字檔案中資料的能力,它會自動給一行中的每個資料元素分配一個變數。

預設情況下,awk 會將如下變數分配給它在文字行中發現的資料欄位:

  • $0 代表整個文字行;
  • $1 代表文字行中的第 1 個資料欄位;
  • $2 代表文字行中的第 2 個資料欄位;
  • $n 代表文字行中的第 n 個資料欄位。

前面說過,在 awk 中,預設的欄位分隔符是任意的空白字元(例如空格或製表符)。 在文字行中,每個資料欄位都是通過欄位分隔符劃分的。awk 在讀取一行文字時,會用預定義的欄位分隔符劃分每個資料欄位。

所以在下面的例子中,awk 程式讀取文字檔案,只顯示第 1 個資料欄位的值:

[root@localhost ~]# cat data2.txt
One line of test text.
Two lines of test text.
Three lines of test text.
[root@localhost ~]# awk '{print $1}' data2.txt
One
Two
Three

該程式用 $1 欄位變數來表示“僅顯示每行文字的第 1 個資料欄位”。當然,如果你要讀取採用了其他欄位分隔符的檔案,可以用 -F 選項手動指定。

awk 指令碼命令使用多個命令

awk 允許將多條命令組合成一個正常的程式。要在命令列上的程式指令碼中使用多條命令,只要在命令之間放個分號即可,例如:

[root@localhost ~]# echo "My name is Rich" | awk '{$4="Christine"; print $0}'
My name is Christine

第一條命令會給欄位變數 $4 賦值。第二條命令會列印整個資料欄位。可以看到,awk 程式在輸出中已經將原文字中的第四個資料欄位替換成了新值。

除此之外,也可以一次一行地輸入程式指令碼命令,比如說:

[root@localhost ~]# awk '{
> $4="Christine"
> print $0}'
My name is Rich
My name is Christine

在你用了表示起始的單引號後,bash shell 會使用 > 來提示輸入更多資料,我們可以每次在每行加一條命令,直到輸入了結尾的單引號。

注意,此例中因為沒有在命令列中指定檔名,awk 程式需要使用者輸入獲得資料,因此當執行這個程式的時候,它會一直等著使用者輸入文字,此時如果要退出程式,只需按下 Ctrl+D 組合鍵即可。

awk從檔案中讀取程式

跟 sed 一樣,awk 允許將指令碼命令儲存到檔案中,然後再在命令列中引用,比如:

[root@localhost ~]# cat awk.sh
{print $1 "'s home directory is " $6}
[root@localhost ~]# awk -F: -f awk.sh /etc/passwd
root's home directory is /root
bin's home directory is /bin
daemon's home directory is /sbin
adm's home directory is /var/adm
lp's home directory is /var/spool/lpd
...
Christine's home directory is /home/Christine
Samantha's home directory is /home/Samantha
Timothy's home directory is /home/Timothy

awk.sh 指令碼檔案會使用 print 命令列印 /etc/passwd 檔案的主目錄資料欄位(欄位變數 $6),以及 userid 資料欄位(欄位變數 $1)。注意,在程式檔案中,也可以指定多條命令,只要一條命令放一行即可,之間不需要用分號。

awk BEGIN關鍵字

awk 中還可以指定指令碼命令的執行時機。預設情況下,awk 會從輸入中讀取一行文字,然後針對該行的資料執行程式指令碼,但有時可能需要在處理資料前執行一些指令碼命令,這就需要使用 BEGIN 關鍵字。

BEGIN 會強制 awk 在讀取資料前執行該關鍵字後指定的指令碼命令,例如:

[root@localhost ~]# cat data3.txt
Line 1
Line 2
Line 3
[root@localhost ~]# awk 'BEGIN {print "The data3 File Contents:"}
> {print $0}' data3.txt
The data3 File Contents:
Line 1
Line 2
Line 3

可以看到,這裡的指令碼命令中分為 2 部分,BEGIN 部分的指令碼指令會在 awk 命令處理資料前執行,而真正用來處理資料的是第二段指令碼命令。

awk END關鍵字

和 BEGIN 關鍵字相對應,END 關鍵字允許我們指定一些指令碼命令,awk 會在讀完資料後執行它們,例如:

[root@localhost ~]# awk 'BEGIN {print "The data3 File Contents:"}
> {print $0}
> END {print "End of File"}' data3.txt
The data3 File Contents:
Line 1
Line 2
Line 3
End of File

可以看到,當 awk 程式列印完檔案內容後,才會執行 END 中的指令碼命令。

相關文章