awk 是一種樣式掃描和處理語言,使用 Linux 的 awk 命令能夠高效快捷地進行文本處理。awk 掃描文本的每一行並執行指定的命令。html
awk 誕生於 1977 年,借鑑了 C 語言等編程語言,名字取自三位設計者 Alfred Aho、Peter Weinberger 和 Brian Kernighan 的姓氏。awk 的版本衆多,本文中使用的是 Ubuntu 上的 GNU Awk,在 MacOS 上可使用 HomeBrew 安裝 gawk。node
awk 能夠直接在命令行中執行,也能夠編寫.awk
後綴的文件而後執行。awk 以行爲單位進行文本處理,對於接收到的每一行都會執行指定的行爲。正則表達式
$ awk [ -F fs ] [ -v var=value ] 'pattern {action}' [ file ... ]
複製代碼
其中 -F
指定分隔符,-v
指定 awk 的內置變量。編程
例如對於 /etc/passwd
文件中的內容:bash
root:x:0:0:root:/root:/usr/bin/zsh
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
複製代碼
若是要輸出每一行的內容,可使用編程語言
$ awk '{print $0}' /etc/passwd
複製代碼
其中 $0
表示掃描到的文本行。函數
.awk
文件能夠分紅三個部分來寫,以下:ui
# passwd.awk
BEGIN{
FS="\n";
print "Before action";
}
{
print $0;
}
END{
print "After action";
}
複製代碼
BEGIN 塊用於定義處理每一行以前的行爲,能夠用來設置 awk 的內置變量,設置好後在後面處理每一行時都會生效。spa
END 塊用於定義處理完文本以後的行爲,能夠用來輸出一些總結信息。命令行
BEGIN 和 END 中間的塊是對於每一行的操做。直接在命令行中執行 awk 時也能夠用 BEGIN 和 END 塊。
寫好文件後在命令行中執行:
Before action
root:x:0:0:root:/root:/usr/bin/zsh
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
After action
複製代碼
$0
表示掃描到的行,$1
表示將該行分隔後的第 1 項,$2
表示將該行分隔後的第 2 項,以此類推。
爲了輸出 /etc/passwd
的用戶名(第一項),能夠執行下面語句:
$ awk -F ':' '{print $1}' /etc/passwd
root
daemon
bin
sys
複製代碼
這裏以處理第一行 root:x:0:0:root:/root:/usr/bin/zsh
爲例,awk 處理是會先按照 -F
設置的分隔符 :
將這行分割爲 root x 0 0 root /root /usr/bin/zsh
而後輸出第一項 root
FS(field separator)
FS 是輸入字段分隔符,例如上面設置的 :
,其默認值是空格,能夠在命令行中使用 -F
設置,也能夠在 BEGIN 塊中經過 FS=
設置爲某個字符串或者一個正則表達式。例如:
$ awk -F ":" '{print $1,$2,$3}' /etc/passwd
root x 0
daemon x 1
bin x 2
sys x 3
複製代碼
OFS(output field separator)
OFS 是輸出字段鏈接符,上面例子中的輸出默認使用空格做爲輸出字段鏈接符,經過設置 OFS 變量進行修改:
$ awk -F ":" -v OFS="-" '{print $1,$2,$3}' /etc/passwd
root-x-0
daemon-x-1
bin-x-2
sys-x-3
複製代碼
RS(record separator)
前面提到的例子中,awk 都默認以行爲單位處理文本,每一行保存了一條記錄,這是由於默認的記錄分隔符 RS 爲 "\n"。還有一些文本在存儲時並非想 csv 等文件同樣以行單位,例如:
# people.txt
P1
male
15
p2
female
20
p3
male
19
複製代碼
上面的文件中使用 "\n\n" 分隔記錄,每一個記錄中又使用 "\n" 分隔字段,能夠這樣處理:
$ awk -F "\n" -v RS="\n\n" '{print $1,$2,$3}' people.txt
P1 male 15
p2 female 20
p3 male 19
複製代碼
ORS(output field separator)
與 RS 相似,ORS 設置輸出的記錄分隔符。
$ awk -F "\n" -v RS="\n\n" -v ORS="\n***\n" '{print $1,$2,$3}' people.txt
P1 male 15
***
p2 female 20
***
p3 male 19
***
複製代碼
NR(number of records)
NR 表示當前正在處理的記錄的是第幾項,若是 NR 出如今 END 塊中則表示已處理的記錄數
$ awk -F ":" '{print "line" NR ":" $1,$2,$3}' /etc/passwd
line1:root x 0
line2:daemon x 1
line3:bin x 2
line4:sys x 3
複製代碼
若是同時處理多個文件,那麼這個項數會累加
$ awk -F ":" '{print "record" NR ":" $1,$2,$3}' people.txt /etc/passwd
record1:P1
record2:male
record3:15
record4:
record5:p2
record6:female
record7:20
record8:
record9:p3
record10:male
record11:19
record12:root x 0
record13:daemon x 1
record14:bin x 2
record15:sys x 3
複製代碼
NF(number of fields)
NF 表示一條記錄中分隔後的字段數,所以這個值與設置的 FS 有關:
# 以 ":" 爲分隔符
$ awk -F ":" '{print "record" NR " with " NF " fields:" $1,$2,$3}' /etc/passwd
record1 with 7 fields:root x 0
record2 with 7 fields:daemon x 1
record3 with 7 fields:bin x 2
record4 with 7 fields:sys x 3
# 以 "o" 爲分隔符
$ awk -F "o" '{print "record" NR " with " NF " fields:" $1,$2,$3}' /etc/passwd
record1 with 7 fields:r t:x:0:0:r
record2 with 5 fields:daem n:x:1:1:daem n:/usr/sbin:/usr/sbin/n
record3 with 3 fields:bin:x:2:2:bin:/bin:/usr/sbin/n l gin
record4 with 3 fields:sys:x:3:3:sys:/dev:/usr/sbin/n l gin
複製代碼
FILENAME
FILENAME 是當前正在處理的文件的名字
$ awk -F ":" '{print FILENAME}' /etc/passwd people.txt
/etc/passwd
/etc/passwd
/etc/passwd
/etc/passwd
people.txt
people.txt
people.txt
people.txt
people.txt
people.txt
people.txt
people.txt
people.txt
people.txt
people.txt
複製代碼
這個值在開始處理記錄後纔有意義,所以在 BEGIN 塊中嘗試輸出 FILENAME 將獲得空值
FNR
前面的 NR 表示的項數在處理多個文件時會累加,而FNR 表示記錄位於當前文件的第幾項
awk -F ":" '{print "record" FNR ":" $1,$2,$3}' people.txt /etc/passwd
record1:P1
record2:male
record3:15
record4:
record5:p2
record6:female
record7:20
record8:
record9:p3
record10:male
record11:19
record1:root x 0
record2:daemon x 1
record3:bin x 2
record4:sys x 3
複製代碼
awk 提供了一些內置函數,便於文本和運算的處理,包括獲取字符串長度的 length()
,獲取隨機數的 rand()
,計算正餘弦的 sin()
和 cos()
。
這些函數能夠在官方手冊中查詢。
上面的全部的例子都對每一條記錄進行了操做,事實上還能夠用條件進行篩選。
使用正則表達式能夠對記錄進行模式匹配:
$ awk -F ':' '/root/ {print $1,$2,$3}' /etc/passwd
root x 0
複製代碼
這裏篩選出了包含 root
的記錄。
結合 awk 的內置變量和函數也能夠進行篩選:
# 輸出第一個字段長度大於 2 且在 第 1 條記錄以後的記錄
$ awk -F ':' 'length($1)>3 && NR>1 {print $0}' /etc/passwd
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
複製代碼
awk 也提供了 if 語句:
# 輸出第 3 個字段爲 0 的記錄
$ awk -F ':' '{if ($3==0)print $0}' /etc/passwd
root:x:0:0:root:/root:/usr/bin/zsh
複製代碼
awk 還有 for 語句,與 C 語言中形式相似:
$ awk -v ORS="," 'BEGIN{ for(i=1;i<5;i++) print i}'
1,2,3,4,
複製代碼
awk 提供了對數學運算符和邏輯運算符的支持的支持,在 awk 中字符串與數字間還能夠直接進行強制類型轉換,+0
能夠強制轉換爲數字,與空格拼接能夠轉換爲字符串:
awk 'BEGIN{print "origin\tnumber\tstring"}{print $0,"\t",$0+0,"\t",$0 ""}' people.txt
origin number string
P1 0 P1
male 0 male
15 15 15
0
p2 0 p2
female 0 female
20 20 20
0
p3 0 p3
male 0 male
19 19 1
複製代碼
這裏使用了 $0 ""
的表示方式將原記錄與空格進行了拼接,但在某些狀況下會出現問題,考慮下面的語句:
$ awk 'BEGIN { print -12 " " -24 }'
-| -12-24
複製代碼
這裏想要在 -12 和 -24 中間加一個空格,但沒有獲得想要的結果,這是因爲數學運算符的優先級高於拼接操做,因此解析順序以下:
-12 (" " - 24)
⇒ -12 (0 - 24)
⇒ -12 (-24)
⇒ -12-24
複製代碼
要獲得正確的正確的結果須要使用括號結合:
$ awk 'BEGIN { print -12 " " (-24) }'
-| -12 -24
複製代碼