1. 程式人生 > 實用技巧 >linux工具-awk

linux工具-awk

目錄

1. 基本知識

1.1. 名稱由來

awk是其設計者名字首字母.

1.2. 語法格式

awk [options] 'pattern {action}' filename

1.3. 執行流程

對如下語句:

awk 'BEGIN {action1} pattern {action2} END {action3}' filename

執行流程如下:

  1. 執行BEGIN {action1}語種塊中的語句.
  2. 從檔案或STDIN逐行讀取, 對每一行執行pattern {action2}, 直到全部讀取完成.
  3. 執行END {action3}語種塊中的語句.

例子: 列印每一行, 並且在開頭和結尾分別列印Start和END.print語句不帶引數時就列印整行內容

例子:對第2列進行累加, 在BEGIN中為i賦初值, 在END中列印i(累加結果).

$ echo -e "a 1 a\na 2 a\na 3 a" | \  
> awk 'BEGIN {i=0} {i+=$2} END {print i}'  
6  

1.4. 術語解釋

術語 說明
記錄 awk把每一個以換行符結束的行稱為一個記錄, NR即記錄的個數
記錄中每個單詞稱做"域", 預設以空格或Tab分隔, NF即域的個數.

2. options

選項 說明
-v 引數傳遞
-f 指定指令碼檔案
-F 指定分隔符
-V 檢視awk版本號

例子1:指定列分隔符

檔案內容

$ cat demo.txt  
root:x:0:0:root:/root:/bin/bash  
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin  
bin:x:2:2:bin:/bin:/usr/sbin/nologin  
sys:x:3:3:sys:/dev:/usr/sbin/nologin  
sync:x:4:65534:sync:/bin:/bin/sync  

通過-F指定列分隔符

$ # -F和:之間沒有空格  
$ awk -F: '{printf "%-6s %-9s %s\n", $1, $6, $7}' demo.txt  
root   /root     /bin/bash  
daemon /usr/sbin /usr/sbin/nologin  
bin    /bin      /usr/sbin/nologin  
sys    /dev      /usr/sbin/nologin  
sync   /bin      /bin/sync  
$   
$ # -F和:之間有空格, 這時分隔符最好放到引號中, 否則使用#等分隔時會報錯.  
$ awk -F ':' '{printf "%-6s %-9s %s\n", $1, $6, $7}' demo.txt  
root   /root     /bin/bash  
daemon /usr/sbin /usr/sbin/nologin  
bin    /bin      /usr/sbin/nologin  
sys    /dev      /usr/sbin/nologin  
sync   /bin      /bin/sync  

通過-v FS指定列分隔符

$ awk -v FS=: '{printf "%-6s %-9s %s\n", $1, $6, $7}' demo.txt  
root   /root     /bin/bash  
daemon /usr/sbin /usr/sbin/nologin  
bin    /bin      /usr/sbin/nologin  
sys    /dev      /usr/sbin/nologin  
sync   /bin      /bin/sync  

3. pattern {action}

awk_script由pattern和Action組成,
例如:

awk 'BEGIN {action1} pattern {action2} END {action3}' filename

模式:有4種模式

  1. /正則表示式/
  2. 關係表示式:使用運算子進行操作, 比如字串、數字的比較測試.
  3. 模式匹配表示式:用運算子:~和~!, 分別表示匹配、不匹配.
  4. BEGIN/END語句塊、pattern語句塊.

action:由一個或多個命令/函式/表示式組成, 它們之間用換行符或分號隔開.
action主要有以下型別:

  1. 變數或陣列賦值
  2. 輸出命令
  3. 內建函式
  4. 控制流語句

注意:

  1. BEGIN {action1}, 只在開始執行一次, 是可選的, 預設無BEGINaction.
  2. pattern {action2}, 對匹配pattern的行進行操作, pattern是可選的, 預設對每一行進行action2, {action2}也是可選的, 預設列印匹配pattern的行, 但是pattern和{action2}必須出現一個, 不能都省略.
  3. END {action3}, 只在結尾執行一次, 是可選的, 預設無END action.

例子1:
輸入檔案內容:

$ cat a.txt  
line first    1  
line second   2  
line third    3  
line forth    4  
line fifth    5  
line sixth    6  
line seventh  7  

使用BEGIN、END等

$ # 在BEGIN的action中, 先列印一行內容, 再給i賦初值  
$ # 在遍歷line的語句中, 列印第2、3列, 並對第3列累加  
$ # 在END的action中, 列印第3列的累加結果  
$ awk 'BEGIN {print "C2", "C3"; i=0} {print $2, $3; i+=$3} END {print "total=",i}' a.txt  
C2 C3  
first 1  
second 2  
third 3  
forth 4  
fifth 5  
sixth 6  
seventh 7  
total= 28  

注意:

  1. 在print語句中,
    字串要寫在雙引號裡,
    字串之間用逗號分隔,
    變數可以直接賦初值,
    變數使用時不能帶雙引號
  2. 變數賦值和使用時不需要帶$號($1/$2等不受此限制)
  3. 多個action語句之間使用分號分隔.

4. 運算子

<tr>
    <td rowspan=1>賦值運算子</td>
    <td>=</td>
    <td>賦值語句</td>
</tr>

<tr><th rowspan=2>邏輯運算子</th><td>||</td><td>邏輯或</td></tr>
<tr>                             <td>&&</td><td>邏輯與</td></tr>

<tr><th rowspan=2>正則運算子</th><td>~  </td><td>正則匹配  </td></tr>
<tr>                             <td>~\!</td><td>正則不匹配</td></tr>

<tr><th rowspan=4>關係運算符</th> <td><, <=</td> <td>小於, 小於等於</td></tr>
<tr>                              <td>>, >=</td> <td>大於, 大於等於</td></tr>
<tr>                              <td>==   </td> <td>等於          </td></tr>
<tr>                              <td>!=   </td> <td>不等於        </td></tr>

<tr><th rowspan=5>算術運算子</th> <td>+, -   </td> <td>加, 減        </td></tr>
<tr>                              <td>*, /, %</td> <td>乘, 除, 取餘  </td></tr>
<tr>                              <td>+, -, !</td> <td>一元加, 減, 非</td></tr>
<tr>                              <td>^, *** </td> <td>冪            </td></tr>
<tr>                              <td>++, -- </td> <td>自增, 自減    </td></tr>

<tr><th rowspan=4>其它運算子</th> <td>$   </td> <td>欄位引用      </td></tr>
<tr>                              <td>空格</td> <td>字串連線    </td></tr>
<tr>                              <td>?:  </td> <td>三元運算      </td></tr>
<tr>                              <td>in  </td> <td>元素存在於陣列</td></tr>
類別 運算子 說明

例: 只處理奇數行並輸出

$ awk -F: 'NR%2==1 {printf "%s) %-6s %-9s %s\n", NR, $1, $(NF-1), $NF}' demo.txt  
1) root   /root     /bin/bash  
3) bin    /bin      /usr/sbin/nologin  
5) sync   /bin      /bin/sync  

例: 只處理$1等於root或bin的行, 使用||

$ awk -F: '$1=="root"||$1=="bin" {printf "%s) %-6s %-9s %s\n", NR, $1, $(NF-1), $NF}' demo.txt  
1) root   /root     /bin/bash  
3) bin    /bin      /usr/sbin/nologin  

5. 內建變數

<tr><th rowspan=2>命令列</th> <td>ARGC</td> <td>命令列引數個數</td></tr>
<tr>                          <td>ARGV</td> <td>命令列引數陣列</td></tr>

<tr><th rowspan=1>檔名</th> <td>FILENAME</td> <td>當前檔名</td></tr>

<tr><th rowspan=3>計數</th> <td>FNR</td> <td>各檔案分別計數的行號</td></tr>
<tr>                        <td>NF </td> <td>當前行的欄位個數(當前行分成了幾列)</td></tr>
<tr>                        <td>NR </td> <td>行號</td></tr>

<tr><th rowspan=4>分隔符</th> <td>FS </td> <td>輸入欄位分隔符, 預設為空白字元</td></tr>
<tr>                          <td>OFS</td> <td>輸出欄位分隔符, 預設為空白字元</td></tr>
<tr>                          <td>RS </td> <td>輸入記錄分隔符(輸入換行符)</td></tr>
<tr>                          <td>ORS</td> <td>輸出記錄分隔符(輸出換行符)</td></tr>

<tr><th rowspan=2>記錄</th> <td>$0</td> <td>完整的輸入記錄</td></tr>
<tr>                        <td>$n</td> <td>當前記錄的第n個欄位, 欄位間由FS分隔</td></tr>
範圍內建變數說明

例: 使用NF, NF表示欄位數, 本例中NF=7, $NF即$7表示最後一個欄位, $(NF-1)表示倒數第二欄位

$ awk -F: '{printf "%-6s %-9s %s\n", $1, $(NF-1), $NF}' demo.txt  
root   /root     /bin/bash  
daemon /usr/sbin /usr/sbin/nologin  
bin    /bin      /usr/sbin/nologin  
sys    /dev      /usr/sbin/nologin  
sync   /bin      /bin/sync  

例: 使用NR, NR表示當前行的行號

$ awk -v FS=: '{printf "%s) %-6s %-9s %s\n", NR, $1, $(NF-1), $NF}' demo.txt  
1) root   /root     /bin/bash  
2) daemon /usr/sbin /usr/sbin/nologin  
3) bin    /bin      /usr/sbin/nologin  
4) sys    /dev      /usr/sbin/nologin  
5) sync   /bin      /bin/sync  

6. 變數

awk中, 變數不需要定義就可以直接使用, 變數型別可以是數字或字串.

例:如果第一個域匹配test, 則把第二第三個域相加並列印.

awk '$1 ~ /test/ {count=$2+$3; print count}' filename

域變數可以修改:
例: 如果第一個域等於"root", 則把它賦值為test並列印.

awk '$1=="root" {$1="test"; print}' filename

7. 條件語句

if語句

如果第三個域大於3, 則列印一個第二第三域和一個"match"字元, 各字元之間可以有空格, 也可以沒有, 字串要寫到雙引號中.

awk '{if ($3>3) print $2 "\t" $3 "\t" "match"}' a.txt

if-else if-else語句
有多個分支, 各分支的語句要寫在大括號中.

awk '{if ($3<=2) {print "0~2"} else if ($3<=4) {print "3~4"} else {print "5~n"}}' a.txt

8. 迴圈語句

  1. awk有三種迴圈:while、for、special for
  2. 可以使用break和continue.
  3. next語句從輸入檔案中讀取一行, 然後從頭開始執行awk指令碼.
  4. exit語句用於結構awk程式, 但不會略過END塊.

例子:

awk '{i=1; while(i<=NF){print i, $i; i++}; print "------"}' a.txt

說明:

  1. NF是當前行(記錄)的欄位(域)數量, $i是第i個欄位(從1開始).
  2. 注意:print語句中, i和$i是不同的東西, i是迴圈變數, $i是第i個欄位.
  3. 本例以欄位數為迴圈條件, 遍歷每個欄位並列印.

例子:

awk '{for (i=1;i<=NF;i++) {print i, $i}; print "------"}' a.txt

說明:作用同上, 將while語句改為for語句實現.

special for用來遍歷關聯陣列.見關聯陣列.for(i in name) {print i, name[i]}

9. 陣列

在awk中資料的下標可以是數字, 也可以是字母(稱為關聯陣列, 類似於python中的字典).

例子:

awk '{name[i++]=$2} END {for (i=0;i<NR;i++) {print i, name[i]}}' a.txt

說明:

  1. 將每行的第二欄位存入陣列, 在結尾遍歷陣列並列印.
  2. name是一個數組, 不用宣告, 直接賦值.
  3. 給陣列賦值時, 直接對下標賦值, 不需要管該下標是否存在.
  4. 陣列下標變數i自動初始化為0, 然後每處理一個記錄自動加1.
  5. NR是當前行的行號(從1開始), 在END塊中NR自動變成總行數, 用來遍歷陣列.

例子:

awk '/s/ {name[NR]=$2} END {for(i in name) {print i, name[i]}}' a.txt

說明:

  1. 如果某行匹配/s/, 則給陣列賦值, 下標是行號, 值是$2, 在結尾遍歷陣列.
  2. name的下標雖然是數字, 但可以是不連續的.

10. 內建函式

https://www.gnu.org/software/gawk/manual/html_node/Built_002din.html#Built_002din

函式 作用
toupper() 字串轉為大寫
tolower() 字串轉為小寫
length() 返回字串長度
substr() 返回子字串
sin() 正弦
cos() 餘弦
sqrt() 平方根
rand() 隨機數

例: 使用toupper()將第一欄位轉為大寫

$ awk -F: '{printf "%s) %-6s %-9s %s\n", NR, toupper($1), $(NF-1), $NF}' demo.txt  
1) ROOT   /root     /bin/bash  
2) DAEMON /usr/sbin /usr/sbin/nologin  
3) BIN    /bin      /usr/sbin/nologin  
4) SYS    /dev      /usr/sbin/nologin  
5) SYNC   /bin      /bin/sync