(五)Linux之文件與目錄管理以及文本處理
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 cp
、help cp
。
切換目錄 cd
cd 命令,是 Change Directory 的縮寫,用來切換工作目錄。
Linux 命令按照來源方式,可分為兩種,分別是 Shell 內置命令和外部命令。所謂 Shell 內置命令,就是 Shell 自帶的命令,這些命令是沒有執行文件的;而外部命令就是由程式設計師單獨開發的,所以會有命令的執行文件。Linux 中的絕大多數命令是外部命令,而 cd 命令是一個典型的 Shell 內置命令,所以 cd 命令沒有執行文件所在路徑。
cd 命令的基本格式如下:
[root@localhost ~]# cd [相對路徑或絕對路徑]
除此之外,cd 命令後面可以跟一些特殊符號,表達固定的含義,如 表 所示:
特殊符號 | 作 用 |
---|---|
~ | 代表當前登錄用戶的主目錄 |
~用戶名 | 表示切換至指定用戶的主目錄 |
– | 代表上次所在目錄 |
. | 代表當前目錄 |
.. | 代表上級目錄 |
它們的用法分別是:
-
[root@localhost vbird]# cd ~ #表示回到自己的主目錄,對於 root 用戶,其主目錄為 /root [root@localhost ~]# cd #沒有加上任何路徑,也代表回到當前登錄用戶的主目錄 [root@localhost ~]# cd ~vbird #代表切換到 vbird 這個用戶的主目錄,亦即 /home/vbird
-
[root@localhost ~]# cd .. #表示切換到目前的上一級目錄,亦即是 /root 的上一級目錄的意思;
需要注意的是,在 Linux 系統中,根目錄確實存在 .(當前目錄)以及 ..(當前目錄的父目錄)兩個目錄,但由於根目錄是最頂級目錄,因此根目錄的 .. 和 . 的屬性和許可權完全一致,也就是說,根目錄的父目錄是自身。
-
[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 列的含義分別是:
- 第一列:規定了不同的用戶對文件所擁有的許可權,具體許可權的含義將在後續章節中講解。
- 第二列:引用計數,文件的引用計數代表該文件的硬鏈接個數,而目錄的引用計數代表該目錄有多少個一級子目錄。
- 第三列:所有者,也就是這個文件屬於哪個用戶。默認所有者是文件的建立用戶。
- 第四列:所屬組,默認所屬組是文件建立用戶的有效組,一般情況下就是建立用戶的所在組。
- 第五列:大小,默認單位是位元組。
- 第六列:文件修改時間,文件狀態修改時間或文件數據修改時間都會更改這個時間,注意這個時間不是文件的創建時間。
- 第七列:文件名或目錄名。
【例 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 個重要的資訊:
- 每個文件都獨自佔用一個 inode,文件內容由 inode 的記錄來指向;
- 如果想要讀取文件內容,就必須藉助目錄中記錄的文件名找到該文件的 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 cp
、help 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
//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 命令可查看文件中的內容
//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中執行指定命令。 |
回車鍵 | 向下移動一行。 |
空格鍵 | 向下移動一頁。 |
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 | 向上移動一行。 |
空格鍵 | 向下移動一頁。 |
回車鍵 | 向下移動一行。 |
【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 會根據腳本命令來處理文本文件中的數據,這些命令要麼從命令行中輸入,要麼存儲在一個文本文件中,此命令執行數據的順序如下:
- 每次僅讀取一行內容;
- 根據提供的規則命令匹配並修改數據。注意,sed 默認不會直接修改源文件數據,而是會將數據複製到緩衝區中,修改也僅限於緩衝區中的數據;
- 將執行結果輸出。
當一行數據匹配完成後,它會繼續讀取下一行數據,並重複這個過程,直到將文件中所有數據處理完畢。
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 種:
- 以數字形式指定行區間;
- 用文本模式指定具體行區間。
以上兩種形式都可以使用如下這 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 中的腳本命令。