Linux 操作系統中有很多文件:配置文件、文本文件、文檔文件、日志文件、用戶文件,這個清單還在不斷增長。通常,這些文件都包含了要查找重要數據所需要訪問的一些信息。盡管我們可以簡單地使用諸如 cat、more 之類的標准工具將大部分文件的內容輸出到屏幕上,但是系統中有更加合適的工具可以對文本進行過濾和處理,這樣就可以只關心我們想要的內容。
在閱讀本文的過程中,您可以打開 shell 並體驗一下每個工具的例子。
正則表達式
在開始之前,我們需要首先理解什麼是正則表達式,以及如何使用正則表達式。
在最簡單的形式中,正則表達式(regular eXPression)是用來在文件中定位文本的一些搜索標准。例如,要查找所有包含單詞 “admin” 的行,我們就可以對 “admin” 進行搜索。因此,“admin” 就構成了一個正則表達式。如果我們不但希望查找 “admin”,而且還想將其替換成 “root”,那麼我們就可以在一個工具中使用適當的命令將 “admin” 替換成 “root”。它們都構成了正則表達式。
正則表達式所采用的一些基本規則如下:
任何單個字符或一串字符都可以匹配字符本身,例如上面的 “admin” 的例子。 ^ 符號(^)表示一行的開始;$ 符號($)表示一行的結束。 要搜索特殊字符(例如 $ 符號),需要在這些字符前面加上反斜線(\)。例如, \$ 就表示查找 $,而不是一行的末尾。 點(.)代表任何單個字符。例如,ad..n 代表 5 個字符項,前兩個字符是 “ad”,最後一個字符是 “n”。中間兩個字符可以是任何字符,但是只能是由兩個字符組成。 任何時候如果正則表達式包含在斜線中(例如 /re/),搜索就是通過文件順序進行的。如果正則表達式包含在問號中(例如,?re?),搜索就是通過文件逆序進行的。 方括號([])表示多個值,減號(-)表示值的范圍。例如,[0-9] 與 [0123456789] 相同,[a-z] 就等效於搜索任何小寫字符。如果一個列表的首字符是 ^ 符號,它就匹配不在這個清單中的任何字符。表 1 給出了這些規則是如何真正進行匹配的。
表 1. 示例正則表達式 例子 說明 [abc] 匹配 “a”、“b”、“c” 之一 [a-z] 匹配從 “a” 到 “z” 的任何一個小寫字符 [A-Z] 匹配從 “A” 到 “Z” 的任何一個大寫字符 [0-9] 匹配從 0 到 9 的任何一個數字 [^0-9] 匹配任何除了 0 到 9 數字范圍內的任何字符 [-0-9] 匹配從 0 到 9 的任何數字,或者是短橫線(-) [0-9-] 匹配從 0 到 9 的任何數字,或者是短橫線(-) [^-0-9] 匹配除從 0 到 9 的數字和短橫線(-)之外的任何字符 [a-zA-Z0-9] 匹配任何字符或數字了解了這些信息,下面讓我們開始看一下相關工具。
grep
grep 工具的工作方式是對文件的每一行搜索給定字符串的首次出現。如果找到了這個字符串,就打印該行的內容;否則就不對該行進行打印。下面這個文件我稱之為 “memo”,闡述了 grep 的用法和結果。
To: All EmployeesFrom: Human ResourcesIn order to better serve the needs of our mass market customers, ABC Publishing is integrating the groups selling to this channel for ABC General Reference and ABC Computer Publishing. This change will allow us to better coordinate our selling and marketing efforts, as well as simplify ABC's relationships with these customers in the areas of customer service, co-op management, and credit and collection. Two national account managers, Ricky Ponting and Greeme Smith, have joined the sales team as a result of these changes.To achieve this goal, we have also organized the new mass sales group into three distinct teams reporting to our current sales Directors, Stephen Fleming and Boris Baker. I have outlined below the national account managers and their respective accounts in each of the teams. We have also hired two new national account managers and a new sales administrator to complete our account coverage. They include:Sachin Tendulkar, who joins us from XYZ Consumer Electronics as a national account manager covering traditional mass merchants.Brian Lara, who comes to us via PQR Company and will be responsible for managing our West Coast territory.Shane Warne, who will become an account administrator for our warehouse clubs business and joins us from DEF division.Effectively, we have seven new faces on board:1. RICKY PONTING2. GREEME SMITH3. STEPHEN FLEMING4. BORIS BAKER5. SACHIN TENDULKAR6. BRIAN LARA7. SHANE WARNEPlease join me in welcoming each of our new team members.舉一個簡單的例子來說,要查找包含 “welcoming” 單詞的行,最好的方法是使用下面的命令行:
# grep welcoming memo Please join me in welcoming each of our new team members.
如果您想查找單詞 “market”,結果會有很大的不同,如下所示:
# grep market memo In order to better serve the needs of our mass market customers, ABC Publishing is integrating the groups selling to this channel for ABC General Reference and ABC Computer Publishing. This change will allow us to better coordinate our selling and marketing efforts, as well as simplify ABC's relationships with these customers in the areas of customer service, co-op management, and credit and collection. Two national account managers, Ricky Ponting and Greeme Smith, have joined the sales team as a result of these changes.
注意我們找到了兩個匹配項:我們希望找的 “market” 和 “marketing”。如果在這個文件中還存在 “marketable” 或 “marketed” 之類的單詞,那麼上面的命令也會顯示包含這些單詞的行的內容。
在 grep 中可以使用通配符和元字符,我強烈建議將它們放到引號中,這樣 shell 就不會將它們解釋成命令了。
要查找所有包含數字的行,請使用下面的命令:
# grep "[0-9]" memo 1. RICKY PONTING 2. GREEME SMITH 3. STEPHEN FLEMING 4. BORIS BAKER 5. SACHIN TENDULKAR 6. BRIAN LARA 7. SHANE WARNE
要查找所有包含 “the” 的行,請使用下面的命令:
# grep the memo In order to better serve the needs of our mass market customers, ABC Publishing is integrating the groups selling to this channel for ABC General Reference and ABC Computer Publishing. This change will allow us to better coordinate our selling and marketing efforts, as well as simplify ABC's relationships with these customers in the areas of customer service, co-op management, and credit and collection. Two national account managers, Ricky Ponting and Greeme Smith, have joined the sales team as a result of these changes. To achieve this goal, we have also organized the new mass sales group into three distinct teams reporting to our current sales directors, Stephen Flemming and Boris Baker. I have outlined below the national account managers and their respective accounts in each of the teams. We have also hired two new national account managers and a new sales administrator to complete our account coverage. They include:
正如您可能已經注意到的一樣,輸出結果中包含了單詞 “these”,還有單詞 “the” 的一些精確匹配。
grep 工具,與很多其他 UNIX/Linux 工具一樣,都是大小寫敏感的,這意味著查找 “The” 和 “the” 會產生完全不同的結果。
# grep The memo To achieve this goal, we have also organized the new mass sales group into three distinct teams reporting to our current sales directors, Stephen Flemming and Boris Baker. I have outlined below the national account managers and their respective accounts in each of the teams. We have also hired two new national account managers and a new sales administrator to complete our account coverage. They include:
如果您想查找一個特定的單詞或短語,但卻不太關心它們的大小寫,那可以使用兩種方法。第一種方法是使用方括號同時查找 “The” 和 “the”,如下所示:
# grep "[T, t]he" memo In order to better serve the needs of our mass market customers, ABC Publishing is integrating the groups selling to this channel for ABC General Reference and ABC Computer Publishing. This change will allow us to better coordinate our selling and marketing efforts, as well as simplify ABC's relationships with these customers in the areas of customer service, co-op management, and credit and collection. Two national account managers, Ricky Ponting and Greeme Smith, have joined the sales team as a result of these changes. To achieve this goal, we have also organized the new mass sales group into three distinct teams reporting to our current sales directors, Stephen Flemming and Boris Baker. I have outlined below the national account managers and their respective accounts in each of the teams. We have also hired two new national account managers and a new sales administrator to complete our account coverage. They include:
第二種方法是使用 -i 選項,這告訴 grep 忽略大小寫的敏感性。
# grep -i the memo In order to better serve the needs of our mass market customers, ABC Publishing is integrating the groups selling to this channel for ABC General Reference and ABC Computer Publishing. This change will allow us to better coordinate our selling and marketing efforts, as well as simplify ABC's relationships with these customers in the areas of customer service, co-op management, and credit and collection. Two national account managers, Ricky Ponting and Greeme Smith, have joined the sales team as a result of these changes. To achieve this goal, we have also organized the new mass sales group into three distinct teams reporting to our current sales directors, Stephen Flemming and Boris Baker. I have outlined below the national account managers and their respective accounts in each of the teams. We have also hired two new national account managers and a new sales administrator to complete our account coverage. They include:
除了 -i 選項之外,還有另外幾個命令行選項可以用來改變 grep 的輸出結果。最常見的選項如下所示:
-c —— 屏蔽正常輸出;相反,打印每個輸入文件的匹配行數。 -l —— 屏蔽正常輸出;相反,打印包含正常輸出內容的每個輸入文件的名字。 -n —— 在每行輸出前面加上該行在輸入文件中的行號作為前綴。 -v —— 將匹配意義進行逆反 —— 即選擇那些不 匹配搜索條件的行。fgrep
fgrep 會對文件搜索某個字符串,並打印包含這個字符串的所有行的內容。與 grep 不同的是,fgrep 搜索的是一個字符串,而不是可以匹配某個表達式的一種模式。fgrep 可以看作是 grep 在以下方面進行了增強:
一次可以搜索多個對象。 fgrep 工具通常速度都比 grep 更快。 我們不能使用 fgrep 來搜索使用模式的正則表達式。假設我們希望從前面的 memo 文件中提取全部由大寫字母組成的名字。為了查找 “STEPHEN” 和 “BRIAN”,我們需要執行兩個單獨的 grep 命令,如下所示:
# grep STEPHEN memo 3. STEPHEN FLEMING # grep BRIAN memo 6. BRIAN LARA
使用 fgrep,只需要一個命令就可以實現相同的任務:
# fgrep "STEPHEN > BRIAN" memo 3. STEPHEN FLEMING 6. BRIAN LARA
注意在這兩項之間需要使用回車符號。如果沒有這個回車符號,搜索就變成了查找一行中的 “STEPHEN BRIAN”。有了這個回車符號之後,它就會查找 “STEPHEN” 的匹配項,以及匹配 “BRIAN” 的項。
還要注意在目標文本兩側必須要放上引號。這樣可以將文本與文件名區分開來。
除了在命令行上指定搜索項之外,我們也可以將它們放到一個文件中,並使用這個文件的內容來搜索其他文件。-f 選項讓我們可以指定一個包含搜索項的主文件,其中可以列出經常搜索的內容。
舉例來說,我們可以想像一個名為 “search_items” 的文件,其中包含了我們希望搜索的兩項內容:
# cat search_items STEPHEN BRIAN
下面的命令在前面的 memo 文件中搜索 “STEPHEN” 和 “BRIAN”:
# fgrep -f search_items memo 3. STEPHEN FLEMING 6. BRIAN LARA
egrep
egrep 是 grep 的一個功能更加強大的版本,它讓我們可以一次搜索多個對象。要搜索的對象是使用回車符(與 fgrep 一樣)或管道符()來分隔的。
# egrep "STEPHEN > BRIAN" memo 3. STEPHEN FLEMING 6. BRIAN LARA # egrep "STEPHEN BRIAN" memo 3. STEPHEN FLEMING 6. BRIAN LARA
上面這兩個命令都可以完成相同的工作。
除了搜索多個目標的功能之外,egrep 還提供了重復搜索和分組搜索的功能:
? 查找問號前面字符的零次匹配或一次匹配。 + 查找加號前面字符的一次或多次匹配。 ( ) 表示一個分組。例如,假設您不記得 Brian 的姓是 “Lara” 還是 “Laras”。
# egrep "LARAS?" memo 6. BRIAN LARA
這次搜索會輸出同時匹配 “LARA” 和 “LARAS” 的項。下面的搜索稍微有些不同:
# egrep "STEPHEN+" memo 3. STEPHEN FLEMING
它可以與 “STEPHEN”、“STEPHENN”、“STEPHENNN” 等匹配。
如果您正在查找一個單詞加上它的一個派生詞,可以在圓括號中包含派生詞的標志字符。
# egrep -i "electron(ic)?s" memo Sachin Tendulkar, who joins us from XYZ Consumer Electronics as a national account manager covering traditional mass merchants.
這會查找可以匹配 “electrons” 的項和可以匹配 “electronics” 的項。
總結一下:
+ 號後面的正則表達式可以匹配這個正則表達式的一次或多次出現。 ? 號後面的正則表達式可以匹配這個正則表達式的零次或一次出現。 使用 符號或回車符分隔開的正則表達式會返回可以與任意一個表達式匹配的字符串。 正則表達式可以放到圓括號 ( ) 中進行分組。 我們可以使用的命令行參數包括 -c、-f、-i、-l、-n 和 -v。
grep 工具:一個真實的例子
grep 系列工具可以用於任何文本格式的系統文件,以便查找某行中的匹配項。例如,要在 /etc/passwd 文件中查找用戶 “root” 的項,可以使用下面的命令:
# grep root /etc/passwd root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin
由於 grep 會查找這個文件中的某個匹配項,因此這個命令會查找到 “root” 和 “operator” 這兩項。如果我們希望只查找用戶名為 “root” 的項,可以將這個命令修改成下面的樣子:
# grep "^root" /etc/passwd root:x:0:0:root:/root:/bin/bash
cut
使用 cut 工具,我們可以將構成文件中數據域的各個列分隔開來。默認的分隔符是制表符,-f 選項可以用來指定希望顯示的域。
舉例來說,假設一個文本文件 “sample” 有三列,其內容如下所示:
one two three four five six seven eight nine ten eleven twelve
現在執行下面的命令:
# cut -f2 sample
這會返回:
two five eight eleven
如果將這個命令修改成:
# cut -f1, 3 sample
這會返回下面的不同結果:
one three four six seven nine ten twelve
這個命令有幾個命令行選項。除了 -f 之外,我們還應該熟悉下面兩個選項:
-c —— 允許我們指定字符而不是域。 -d —— 允許我們指定其他分隔符,而不是制表符。cut:兩個實際例子
ls -l 命令可以顯示某個目錄中所有文件的權限、鏈接個數、屬主、組、大小、日期和文件名 —— 這些都是以空格分隔開的。如果我們對大部分域都不感興趣,而是只希望了解文件屬主的信息,可以使用下面的命令:
# ls -l cut -d" " -f5 root 562 root root root root root root
這個命令只會顯示文件屬主(第 5 個域),而會忽略其他域。
如果您知道文件屬主信息開始的第一個字符的確切位置,可以使用 -c 選項來顯示文件屬主的第一個字符。假設它是從第 16 個字符開始的,下面這個命令就返回第 16 個字符,這是文件屬主名的第一個字符。
# ls -l cut -c16 r r r r r r r
如果我們再假設大部分用戶都使用最多 8 個字符作為自己的用戶名,那麼我們就可以使用下面的命令:
# ls -l cut -c16-24
這會返回用戶名域的那些項。
現在假設文件名是從第 55 個字符開始的,但是我們無法確定文件名會連續占用多少個字符,因為有些文件名可能會比其他文件名長很多。解決方案是從第 55 個字符開始,但卻不指定結束字符(這意味著我們要截取該行中所有剩余的內容),如下所示:
# ls -l cut -c55- a.out cscope-15.5 cscope-15.5.tar cscope.out memo search_items test.c test.s
現在我們來考慮另外一種情況。為了獲得系統中所有用戶的清單,我們可以從前面使用過的 /etc/passwd 文件中提取第一個域:
# cut -d":" -f1 /etc/passwd root bin daemon adm lp sync shutdown halt mail news uUCp operator
要搜集用戶名及其對應的主目錄,我們可以提取第 1 個和第 6 個域的內容:
# cut -d":" -f1,6 /etc/passwd root:/root bin:/bin daemon:/sbin adm:/var/adm lp:/var/spool/lpd sync:/sbin shutdown:/sbin halt:/sbin mail:/var/spool/mail news:/etc/news uucp:/var/spool/uucp operator:/root
paste
paste 工具可以對文件中的域進行合並。它從每個源文件中提取一行內容,並將其與另外一個源文件中的一行內容合並在一起。
舉例來說,假設文件 “fileone” 的內容如下所示:
IBM Global Services
另外,我們還有一個 “filetwo” 文件,其內容如下所示:
United States United Kingdom India
下面的命令將這兩個文件的內容合並在一起,如下所示:
# paste fileone filetwo IBM United States Global United Kingdom Services India
如果 fileone 中的行數比 filetwo 多,那麼 paste 操作依然會繼續,不過在制表符後面是一些空項。
制表符字符是默認的分隔符,但是我們可以使用 -d 選項將其修改成任何其他值。
# paste -d", " fileone filetwo IBM, United States Global, United Kingdom Services, India
我們也可以使用 -s 選項將 fileone 的內容在一行中輸出,後面加上一個回車鍵,然後再顯示 filetwo 的內容。
# paste -s fileone filetwo IBM Global Services United States United Kingdom India
join
join 是 paste 的一個很好的增強版本。join 只有在所要連接的文件共享某個共同的域時才會工作。
舉例來說,考慮我們上面介紹 paste 時所使用的兩個文件。下面是在使用 join 對其進行合並時所發生的事情:
# join fileone filetwo
注意這並沒有顯示任何東西。join 工具必須要在所操作的文件之間找到共同的域,默認情況下,它期望這個共同的域就是第一個域。
要了解這是如何工作的,我們可以嘗試添加一些新內容。假設 fileone 現在包含以下內容:
aaaa Jurassic Park bbbb AI cccc The Ring dddd The Mummy eeee Titanic
filetwo 現在包含以下內容:
aaaa Neil 1111 bbbb Steven 2222 cccc Naomi 3333 dddd Brendan 4444 eeee Kate 5555
現在,再次嘗試下面的命令:
# join fileone filetwo aaaa Jurassic Park Neil 1111 bbbb AI Steven 2222 cccc The Ring Naomi 3333 dddd The Mummy Brendan 4444 eeee Titanic Kate 5555
此時第一個域相同的地方就會被識別出來,匹配項也就進行了合並。paste 是盲目地從每個文件中提取內容並創建輸出結果;而 join 則是只合並那些匹配的行,這種匹配必須非常精確。舉例來說,假設您向 filetwo 文件中添加一行內容:
aaaa Neil 1111 bbbb Steven 2222 ffff Elisha 6666 cccc Naomi 3333 dddd Brendan 4444 eeee Kate 5555
現在這個命令只會產生下面的輸出結果了:
# join fileone filetwo aaaa Jurassic Park Neil 1111 bbbb AI Steven 2222
只要這兩個文件不再匹配了,就不會再執行任何操作了。第一個文件的每一行都匹配且只匹配於第二個文件相同行的默認域。如果找到匹配項,它們就會組成輸出;否則就不再執行任何操作了。
默認情況下,join 只會查找第一個域進行匹配,並輸出所有列的內容;不過我們可以對這種行為進行修改。-1 選項讓我們可以指定使用哪個域作為 fileone 中的匹配項, -2 選項讓我們可以指定使用哪個域作為 filetwo 中的匹配項。
舉例來說,要對 fileone 的第二個域和 filetwo 的第三個域進行匹配,我們可以使用下面的語法:
# join -1 2 -2 3 fileone filetwo
-o 選項可以以 {file.field} 格式來指定輸出結果。因此,要在匹配行上打印 fileone 的第二個域和 filetwo 的第三個域,語法為:
# join -o 1.2 -o 2.3 fileone filetwo
join:一個實際的例子
在實踐中可以使用 join 工具最明顯的方法是從 /etc/passwd 中提取用戶名和對應的主目錄項,並從 /etc/group 文件中提取組名。組名在 /etc/passwd 文件中是以數字的格式出現在第四個域中的。類似地,它們在 /etc/group 文件中是在第三個域中出現的。
# join -1 4 -2 3 -o 1.1 -o 2.1 -o 1.6 -t":" /etc/passwd /etc/group root:root:/root bin:bin:/bin daemon:daemon:/sbin adm:adm:/var/adm lp:lp:/var/spool/lpd nobody:nobody:/ vcsa:vcsa:/dev rpm:rpm:/var/lib/rpm nscd:nscd:/ ident:ident:/home/ident netdump:netdump:/var/crash sshd:sshd:/var/empty/sshd rpc:rpc:/
awk
awk 是 Linux 上功能最為強大的工具之一。它本身實際上是一種編程語言,可以實現復雜的邏輯語句,還可以簡化部分文本的提取。在本文那中我們將不會詳細對其進行介紹,而是快速了解一下它的語法,並嘗試幾個實際的例子。
awk 命令包括一個模式和由一條或多條語句構成的操作,語法如下所示:
awk '/pattern/ {action}' file
請注意:
awk 測試指定文件中的每個記錄是否符合模式匹配。如果找到匹配項,就執行指定的操作。 awk 可以在管道中作為過濾器,如果沒有指定文件,它也可以從鍵盤(標准輸入)中接收輸入。一種非常有用的操作是打印數據!下面是如何引用一條記錄中的域。
$0 —— 整條記錄 $1 —— 該記錄中的第一個域 $2 —— 該記錄中的第二個域我們還可以從一條記錄中提取多個域,之間使用逗號分開。
舉例來說,要提取 /etc/passwd 文件中的第 6 個域,命令如下:
# awk -F: '{print $6}' /etc/passwd /root /bin /sbin /var/adm /var/spool/lpd /sbin /sbin /sbin /var/spool/mail /etc/news /var/spool/uucp
注意 -F 是由預先定義的 FS 變量所定義的輸入域分隔符。在我們這個例子中是空格。
要從 /etc/passwd 文件中提取第一個和第六個域,命令如下:
# awk -F: '{print $1,$6}' /etc/passwd root /root bin /bin daemon /sbin adm /var/adm lp /var/spool/lpd sync /sbin shutdown /sbin halt /sbin mail /var/spool/mail news /etc/news uucp /var/spool/uucp operator /root
要在域之間使用短橫線代替冒號來打印這個文件的內容,命令如下:
# awk -F: '{OFS="-"}{print $1,$6}' /etc/passwd root-/root bin-/bin daemon-/sbin adm-/var/adm lp-/var/spool/lpd sync-/sbin shutdown-/sbin halt-/sbin mail-/var/spool/mail news-/etc/news uucp-/var/spool/uucp operator-/root
要使用短橫線作為域之間的分隔符來打印文件,並且只以逆序打印第一個域和第六個域,命令如下:
# awk -F: '{OFS="-"}{print $6,$1}' /etc/passwd /root-root /bin-bin /sbin-daemon /var/adm-adm /var/spool/lpd-lp /sbin-sync /sbin-shutdown /sbin-halt /var/spool/mail-mail /etc/news-news /var/spool/uucp-uucp /root-operator
head
head 工具打印每個文件的最開始部分的內容(默認是 10 行)。如果沒有給定文件,它就從標准輸入中讀入內容,如果給定了文件名就從文件中讀入內容。
舉例來說,如果我們希望從 memo 文件中提取前兩行內容,命令如下:
# head -2 memo In order to better serve the needs of our mass market customers, ABC Publishing is integrating the groups selling to this channel for ABC General Reference and ABC Computer Publishing. This change will allow us to better coordinate our selling and marketing efforts, as well as simplify ABC's relationships with these customers in the areas of customer service, co-op management, and credit and collection. Two national account managers, Ricky Ponting and Greeme Smith, have joined the sales team as a result of these changes.
我們可以使用 -c 選項指定要顯示的字節個數。舉例來說,如果我們希望從 memo 文件中讀取前兩個字節的內容,可以使用下面的命令:
# head -c 2 memo In
tail
tail 工具打印每個文件的最末尾部分的內容(默認是 10 行)。如果沒有給定文件,它就從標准輸入中讀入內容,如果給定了文件名就從文件中讀入內容。
舉例來說,如果我們希望從 memo 文件中提取最後兩行內容,命令如下:
# tail -2 memo Please join me in welcoming each of our new team members.
我們可以使用 -c 選項指定要顯示的字節個數。舉例來說,如果我們希望從 memo 文件中讀取最後五個字節的內容,可以使用下面的命令:
# tail -c 5 memo ers.
結束語
現在我們已經知道如何使用各種工具從標准 Linux 文件中向外提取數據了。一旦提取出數據之後,這些數據就可以進行查看、打印或重定向到其他文件或數據庫中了。了解了如何使用這些有用的工具可以幫助我們減少花費在煩雜任務上的時間,從而能夠成為一名效率更高的管理員。