效能工具之linux三劍客awk、grep、sed詳解

Zee_7D發表於2021-06-23

前言

linux 有很多工具可以做文字處理,例如:sort, cut, split, join, paste, comm, uniq, column, rev, tac, tr, nl, pr, head, tail.....,學習 linux 文字處理的懶惰方式(不是最好的方法)可能是:只學習grep,sed和awk。 

使用這三個工具,你可以解決近 99% linux 系統的文字處理問題,而不需要記住上面不同的命令和引數。圖片

而且,如果你已經學會並使用了三者,你就會知道其中的差異。實際上,這裡的差異意味著哪個工具擅長解決什麼樣的問題。

一種更懶惰的方式可能是學習指令碼語言(python,perl或ruby)並使用它進行每個文字處理。

概述

awk、grep、sed 是 linux 操作文字的三大利器,也是必須掌握的 linux 命令之一。

三者的功能都是處理文字,但側重點各不相同,其中屬 awk 功能最強大,但也最複雜。grep 更適合單純的查詢或匹配文字,sed 更適合編輯匹配到的文字,awk 更適合格式化文字,對文字進行較複雜格式處理。

簡單概括:

  • grep:資料查詢定位

  • awk:資料切片

  • sed:資料修改

grep = global regular expression print

用最簡單術語來說,grep(全域性正規表示式列印)--命令用於查詢檔案裡符合條件的字串。 從檔案的第一行開始,grep 將一行復制到 buffer 中,將其與搜尋字串進行比較,如果比較通過,則將該行列印到螢幕上。grep將重複這個過程,直到檔案搜尋所有行。

 注意:這裡沒有程式執行 grep 儲存行、更改行或僅搜尋部分行。

示例資料檔案

請將以下資料剪下貼上到一個名為 “sampler.log” 的檔案中:

  1. boot

  2. book

  3. booze

  4. machine

  5. boots

  6. bungie

  7. bark

  8. aardvark

  9. broken$tuff

  10. robots

一個簡單例子

grep 最簡單的例子是:

  1. grep "boo" sampler.log

在本例中,grep 將遍歷檔案 “sampler.log” 的每一行,並列印出其中的每一行 包含單詞“boo”:

  1. boot

  2. book

  3. booze

  4. boots

但是如果你操作的是大型檔案,就會出現這種情況:如果這些行標識了檔案中的哪一行,它們是什麼,可能對你更有用,如果需要在編輯器中開啟檔案,那麼可以更容易地跟蹤特定字串做一些改變。 這時候可以通過新增 -n 引數來實現:

  1. grep -n "boo" sampler.log

這產生了一個更有用的結果,解釋了哪些行與搜尋字串匹配:

  1. 1:boot

  2. 2:book

  3. 3:booze

  4. 5:boots

另一個有趣的引數是 -v,它會列印出相反的結果。換句話說,grep 將列印所有與搜尋字串不匹配的行,而不是列印與之匹配的行。 

在下列情況下,grep 將列印不包含字串 “boo” 的每一行,並顯示行號,如上一個例子所示

  1. grep -vn "boo" sampler.log

  2. 4:machine

  3. 6:bungie

  4. 7:bark

  5. 8:aardvark

  6. 9:broken$tuff

  7. 10:robots

c 選項告訴 grep 抑制匹配行的列印,只顯示匹配行的數量,匹配查詢的行。 例如,下面將列印數字4,因為有4個在 sampler.log 中出現 “boo”。

  1. grep -c "boo" sampler.log

  2. 4

l 選項只列印查詢中具有與搜尋匹配行的檔案的檔名字串。 如果你想在多個檔案中搜尋相同的字串,這將非常有用。像這樣:

  1. grep -l "boo" *

對於搜尋非程式碼檔案,一個更有用的選項是 -i,忽略大小寫。這個選項將處理在匹配搜尋字串時,大小寫相等。 在下面的例子中,即使搜尋字串是大寫的,包含“boo”的行也會被列印出來。

  1. grep -i "BOO" sampler.log

  2. boot

  3. book

  4. booze

  5. boots

x 選項只精確匹配。換句話說,以下命令搜尋沒有結果,因為沒有一行只包含"boo"

  1. grep -x "boo" sampler.log

最後,-A 允許你指定額外的上下檔案行,這樣就得到了搜尋字串額外行,例如

  1. grep -A2 "mach" sampler.log

  2. machine

  3. boots

  4. bungie

正規表示式

正規表示式是描述文字中複雜模式的一種緊湊方式。

有了 grep 你可以使用搜尋模式( pattern ) 。其他工具使用正規表示式 (regexp) 以複雜的方式。而 grep 使用的普通字串,實際上非常簡單正規表示式。如果您使用萬用字元,如 ' * ' 或 ' ? ',比如列出檔名等,你可以使用 grep 使用基本的正規表示式進行搜尋 

例如搜尋檔案以字母 e 結尾的行:

  1. grep "e$" sampler.log

  2. booze

  3. machine

  4. bungie

如果需要更廣泛的正規表示式命令,則必須使用 grep-E。 

例如,正規表示式命令 ? 將匹配1或0次出現 之前的字元:

  1. grep -E "boots?" sampler.log

  2. boot

  3. boots

你還可以使用 pipe(|) 結合多個搜尋,它的意思是 “或者”,所以你可以這樣做:

  1. grep -E "boot|boots" sampler.log

  2. boot

  3. boots

特殊字元

如果你想搜尋的是一個特殊字元,該怎麼辦?如果你想找到所有的直線,如果包含美元字元“$”,則不能執行 grep“$”a_file,因為 '$' 將被解釋為正規表示式,相反,你將得到所有的行,其中有任何作為行結束,即所有行。 解決方案是 “轉義” 符號,所以您將使用

  1. grep '\$' sampler.log

  2. broken$tuff

你還可以使用 “-F” 選項,它代表“固定字串”或“快速”,因為它只搜尋字串,而不是正規表示式。

更多的 regexp 的例子

參考:http://gnosis.cx/publish/programming/regular_expressions.html

AWK

由 Aho,Weinberger 和 Kernighan 建立的文字模式掃描和處理語言。 

AWK非常複雜,所以這不是一個完整的指南,但應該給你一個知道什麼 awk 可以做。它使用起來比較簡單,強烈建議使用。

AWK 基礎知識

awk 程式對輸入檔案的每一行進行操作。它可以有一個可選的 BEGIN{ } 部分在處理檔案的任何內容之前執行的命令,然後主{ }部分執行在檔案的每一行中,最後還有一個可選的END{ }部分操作將在後面執行檔案讀取完成:

  1. BEGIN { …. initialization awk commands …}

  2. { …. awk commands for each line of the file…}

  3. END { …. finalization awk commands …}

對於輸入檔案的每一行,它會檢視是否有任何模式匹配指令,在這種情況下它僅在與該模式匹配的行上執行,否則它在所有行上執行。 這些 'pattern-matching' 命令可以包含與 grep 一樣的正規表示式。 

awk 命令可以做一些非常複雜的數學和字串操作,awk也支援關聯陣列。 AWK 將每條線視為由多個欄位組成,每個欄位由“間隔符”分隔。 預設情況下,這是一個或多個空格字元,因此行:

  1. this is a line of text

包含6個欄位。在 awk 中,第一個欄位稱為 $1,第二個欄位稱為 $2,等等,全部行稱為 $0。 

欄位分隔符由 awk 內部變數 FS 設定,因此如果您設定 FS= ": "則它將根據 ':' 的位置劃分一行,這對於 /etc/passwd 之類的檔案很有用,其他有用的內部變數是 NR,即當前記錄號(即行號) NF是當前行中欄位的數量。 

AWK 可以對任何檔案進行操作,包括 std-in,在這種情況下,它通常與 '|' 命令一起使用,例如,結合 grep 或其他命令。 

例如,如果我列出當前目錄中的所有檔案

  1. ls -l

  2. 總用量 140

  3. -rw-r--r-- 1 root root 55121 1月   3 17:03 combined_log_format.log

  4. -rw-r--r-- 1 root root 80644 1月   3 17:03 combined_log_format_w_resp_time.log

  5. -rw-r--r-- 1 root root    71 1月   3 17:55 sampler.log

` 我可以看到檔案大小報告為3 列資料。如果我想知道它們的總大小,這個目錄中的檔案我可以做:

  1. ls -l | awk 'BEGIN {sum=0} {sum=sum+$5} END {print sum}'

  2. 135836

請注意,'print sum' 列印變數 sum 的值,因此如果 sum = 2 則 'print sum' 給出輸出 '2' 而 'print $ sum' 將列印 '1' ,因為第二個欄位包含值 '1' 。 

因此,會很簡單編寫一個可以計算平均值的和一列數字的標準偏差的 awk 命令 - 在主要內部積累 'sumx' 和 'sumx2' 部分,然後使用標準公式計算 END 部分的平均值和標準偏差。 

AWK 支援('for' 和 'while')迴圈和分支(使用 'if ')。 所以,如果你想修剪一個檔案並且只在每個第 3 行操作,你可以這樣做:

  1. ls -l | awk '{for (i=1;i<3;i++) {getline}; print NR,$0}'

  2. 3 -rw-r--r-- 1 root root 80644 1月   3 17:03 combined_log_format_w_resp_time.log

  3. 4 -rw-r--r-- 1 root root    71 1月   3 17:55 sampler.log

for 迴圈使用 “getline” 命令遍歷檔案,並且每隔3次才列印一行。

注意,由於檔案的行數是4,不能被3整除,所以最後一個命令提前完成,所以最後的 “print $0” 命令列印第4行,你可以看到我們也列印了行,使用 NR 變數輸出行號。

AWK 模式匹配

AWK 是一種面向行的語言。首先是模式,然後是動作。 操作語句用{ and }括起來。模式可能缺失,或者動作可能缺失,但是,當然不是都。 如果缺少模式,則對每個輸入記錄執行操作。一個丟失的動作將列印整個記錄。 

AWK 模式包括正規表示式(使用與“grep -E”相同的語法)和使用的組合特殊符號 “&&” 表示“邏輯AND ”,“||”表示“邏輯或”,“!” 的意思是“邏輯不”。 

你也可以做關係模式、模式組、範圍等。

AWK 控制語句

  1. if (condition) statement [ else statement ]

  2. while (condition) statement

  3. do statement while (condition)

  4. for (expr1; expr2; expr3) statement

  5. for (var in array) statement

  6. break

  7. continue

  8. exit [ expression ]

AWK 輸入/輸出語句

注意:printf 命令允許你使用類似 C 的語法更密切地指定輸出格式 例如,你可以指定給定寬度的整數,浮點數或字串等。

AWK 數學函式

AWK 字串函式

AWK 命令列和用法

你可以根據需要多次使用 ' -v ' 標誌將變數傳遞給 awk 程式,例如

  1. awk -v skip=3 '{for (i=1;i<skip;i++) {getline}; print $0}' sampler.log

  2. booze

  3. bungie

  4. broken$tuff

你還可以使用編輯器編寫 awk 程式,然後將其另存為指令碼檔案,例如:

  1. $ cat awk_strip

  2. #!/usr/bin/awk -f

  3. #only print out every 3rd line of input file

  4. BEGIN {skip=3}

  5. {for (i=1;i<skip;i++)

  6. {getline};

  7. print $0}

然後可以將其用作新的附加命令

  1. chmod u+x awk_strip

  2. ./awk_strip sampler.dat

sed = stream editor

sed 對輸入流(檔案或來自管道的輸入)執行基本文字轉換單通過流,所以效率很高。 但是, sed 能夠管道過濾文字,特別區別於其他型別的編輯器。

sed 基礎

sed 可以在命令列或 shel l指令碼中使用,以非互動方式編輯檔案。 也許最有用的功能是對一個字串進行 “搜尋和替換” 到另一個字串。 您可以將 sed 命令嵌入到使用 '-e' 選項呼叫 sed 的命令列中,或者將它們放在一個單獨的檔案中 'sed.in' 並使用 '-f sed.in' 選項呼叫 sed。 後一種選擇是如果 sed 命令很複雜並涉及大量regexp,則最常用,例如: 

sed-e's/input/output/'sampler.log 

將從 sampler.log 回顯到標準輸出的每一行,改變每一行的 'input' 排成 'output'。 注意 sed 是面向行的,所以如果你想改變每一行的每一個事件,那麼你需要讓它成為一個 '貪婪' 的搜尋和替換,如下所示:

  1. sed -e 's/input/output/g' sampler.log

  2. boot

  3. book

  4. booze

  5. machine

  6. boots

  7. bungie

  8. bark

  9. aardvark

  10. broken$tuff

  11. robots

/.../ 中的表示式可以是文字字串或正規表示式。 注意預設情況下,輸出將寫入 stdout。 你可以將其重定向到新檔案,或者如果你願意 編輯現有檔案,你應該使用 '-i' 標誌:

  1. sed -e 's/input/output/' sampler.log  > new_file

  2. sed -i -e 's/input/output/' sampler.log  

sed 和正規表示式

如果你希望在搜尋命令中使用的某個字元是特殊符號,例如 '/',該怎麼辦?(例如在檔名中)或 '*' 等? 然後你必須像 grep(和awk)那樣轉義符號。 跟你說想要編輯shell指令碼以引用 /usr/local/bin而不是 /bin,那麼你可以這樣做

  1. sed -e 's/\/bin/\/usr\/local\/bin/' my_script > new_script

如果你想在搜尋中使用萬用字元怎麼辦 - 如何編寫輸出字串? 你需要使用與找到的模式對應的特殊符號“&”。 所以說你想要每行以你的檔案中的數字開頭,並用括號括起該數字:

  1. sed -e 's/[0-9]*/(&)/'

其中 [0-9] 是所有個位數的 regexp 範圍,而 '*' 是重複計數,表示任何數字的位數。 你還可以在 regexp 中使用位置指令,甚至可以將部分匹配結果儲存在模式緩衝區,以便在其他地方重用。

其它 SED 命令

一般形式是

  1. sed -e '/pattern/ command' sampler.log

其中 'pattern' 是正規表示式,'command' 可以是 's'= search&replace,或 'p'= print,或 'd'= delete,或 'i'=insert,或 'a'=append 等。請注意,預設操作是列印所有不是無論如何匹配,所以如果你想抑制它,你需要使用 '-n' 標誌呼叫 sed,然後你可以使用 'p' 命令來控制列印的內容。 所以,如果你想做一個所有的列表 你可以使用的子目錄

  1. ls -l | sed -n -e '/^d/ p'

因為長列表開始每行都帶有 'd' 符號,如果它是一個目錄,所以這隻會列印出來那些以 'd' 符號開頭的行。 同樣,如果你想刪除所有以評論符號 '#' 開頭的行,你可以使用

  1. sed -e '/^#/ d' sampler.log

也可以使用範圍表單

  1. sed -e '1,100 command' sampler.log

在第1-100 行執行“命令”。你也可以用特殊的行號 $ 來表示“結束”檔案。 因此,如果你想刪除檔案的前10行以外的所有行,您可以使用

  1. sed -e '11,$ d' sampler.log

你還可以使用模式範圍表單,其中第一個正規表示式定義範圍的開始,以及第二站。 所以,例如,如果你想列印從 'boot' 到 'machine' 的所有行 你可以這樣做:

  1. sed -n -e '/boot$/,/mach/p' sampler.log

  2. boot

  3. book

  4. booze

  5. machine

然後只列印出(-n)regexp 給定的給定範圍內的那些行。

總結

Linux 三劍客 awk,sed和grep 在效能領域廣泛用於效能建模、效能監控及效能分析等方面,也是各大網際網路公司測試崗高頻面試題,中高階測試人員必備技能之一

相關文章