常用指令 – awk

awk 是個很強大的工具, 透過其特製的語法可以在很多變化上處理pipe或檔案內容;之前一段時間不碰後,又忘了之前使用過awk語法,趁最近又開始接觸來重新複習這個工具。

測試用檔案 – log.txt

假設一個log檔的檔名為log.txt,且內容如下:

2 this is a test 
3 Are you like awk 
This's a test
10 There are orange,apple,mongo

基本搭配print做格式化輸出

NOTE:

  • 值得注意的是,$0awk中代表的是整行文字
# 在print使用逗號,可以在輸出時用空白隔開$1與$4
awk '{print $1,$4}' log.txt

(上述指令針對每一行的log,截取第1個與第4個讀取到的文字)

2 a
3 like
This's
10 orange,apple,mongo

透過-F 更改分割字元

由於預設awk是使用空白當分割字元來處理同一行文字的,若要改變分割字元,則可以使用-F 這個flag。

aws -F , '{print $1,$2}' log.txt

(上述指令針對每一行的log,透過,分隔以後,截取分割後的第1個與第2個讀取到的文字)

2 a
3 like
This's
10 orange apple

(以這個log檔來看,只有最後一行才會被分割成多個arguments)

透過-v 來代入自訂的變數

透過預先定義好的變數

awk -v a=1 -v b=2 '{print $1,$1+a,b}' log.txt

(上述指令定義了a, b 2個變數與值,且在print時使用它們)

2 3 2
3 4 2
This's 1 2
10 11 2

透過運算符號來使用awk

支援的運算符號可以參考這裡

對每一行文字,過濾只有第一個參數大於2
awk '$1>2' log.txt

# output
3 Are you like awk
This's a test
10 There are orange,apple,mongo
對每一行文字,過濾只有第一個參數大於2,且第二個參數必須是"Are"
awk '$1>2 && $2=="Are" {print $1,$2,$3}' log.txt

# output
3 Are you
對每一行文字,列出只有字元數大於20的行
awk 'length>20 {print $0}' log.txt

# output
10 There are orange,apple,mongo

搭配Regular expression使用

awk中,可以透過運算符號~!~來去搭配正規表示法使用。

對於每一行文字,當第一組參數不是數字時才會被print出來
awk '$1 !~ /[0-9]/ {print $0}' log.txt

# output
This's a test

對於每一行文字,只有當第一組參數是數字時才會被print
awk '$1 ~ /[0-9]/ {print $0}' log.txt

# output
2 this is a test
3 Are you like awk
10 There are orange,apple,mongo

對於每一行文字,只有符合正規表示法時才會被print

如果只是要對整行文字作匹配的話,則可以直接用這方式

awk '/re/ ' log.txt

#output
3 Are you like awk
10 There are orange,apple,mongo

或加個!來過濾掉匹配到的每一行文字

awk '!/re/ ' log.txt

#output
2 this is a test
This's a test

搭配awk的進階使用

awk常見可以使用的keyword可以參考這裡

過濾掉第一行的文字(通常可用於csv檔拿掉header)
awk 'NR != 1' log.txt

# output
3 Are you like awk
This's a test
10 There are orange,apple,mongo

(我猜NR指的應該是Nnumb of Row)

對文字檔的特定參數做數字加總,並且print出來
awk '{sum+=$1} END {print sum}' log.txt

# output
15

(如果每一行的第一個參數是數字的話,就會對它做加總並暫存在sum這個變數上)

針對文字檔中的每一行,來做分組與處理

假設我們有個文字檔如下,然後我們想要針對每個Category的值串起來在印出來

Category: OS
Windows
Liux
Unix
Category: CPU
x86
arm64
amd64
Category: RAM
1GB
2GB
4GB
8GB
16GB

awk '/Category/ {header=$0; if (v) print v; v=""; next} { v=(!v) ? header"="$0 : v","$0;} END {print v;}' support_sepcs.txt

# output
Category: OS=Windows,Liux,Unix
Category: CPU=x86,arm64,amd64
Category: RAM=1GB,2GB,4GB,8GB,16GB

這個指令用了到幾個進階的功能:

  • /Category/ 用來匹配當遇到Category那一行的文字時,會在{header=$0 ...;next}中處理。(結尾的next是指說跳過後面的{v=(!v)…}相關的工作)
  • () ? : 用三元運算符號來去給v這個變數值。
  • END 用來處理最後的收尾。
  • 整個指令的邏輯是,當如果遇到文字中含Category時,則那行的文字會當作是接下去的header,然後如果v 目前是有值的情況下,則print v
    (這個情況v會是前面的字串組成的結果, 例如處理Category: CPU時,v這時會有值,且值會是Category: OS=Windows,Liux,Unix)。
    若沒遇到Category的話,則繼續append 目前的這一行文字到v這個變數上。
    最後一個Category: RAM則是透過 END { print v}來去print出來。
  • 如果在header後想要多處理else的話,會是這樣的語法:
    {header=$0; {if (v) { print v; v=""; next } else {v="";next}}

總結

整個awk的進階使用還更複雜,尤其是它支援許多語法,透過它的語法可以寫成另一個awk的script了,然後在指令中讀取來使用,像是awk -f cal.awk log.txt

reference