一、awk介紹
awk 是一種程式語言. 它具有一般程式語言常見的功能.
因awk語言具有某些特點, 如 : 使用直譯器(Interpreter)不需先行編譯; 變數無型別之分(Typeless), 可使用文字當陣列的下標(Associative Array)...等特色. 因此, 使用awk撰寫程式比起使用其它語言更簡潔便利且節省時間. awk還具有一些內建功能, 使得awk擅於處理具資料行(Record), 欄位(Field)型態的資料; 此外, awk內建有pipe的功能, 可將處理中的資料傳送給外部的 Shell命令加以處理, 再將Shell命令處理後的資料傳回awk程式, 這個特點也使得awk程式很容易使用系統資源。
1.1、awk讀取資料設定
awk讀取輸入檔案時,每次讀取一條記錄(record)(預設情況下按行讀取,所以此時記錄就是行)。每讀取一條記錄,將其儲存到$0中,然後執行一次main程式碼段
在讀取資料時,可設定表示輸入記錄分隔符的預定義變數RS(Record Separator)來改變每次讀取的記錄模式
awk 'BEGIN{RS="\n"}'預設情況下使用\n換行符進行分隔讀取。
RS="":按段落讀取
RS="\0":一次性讀取所有資料,但有些特殊檔案中包含了空字元\0
RS="^$":真正的一次性讀取所有資料,因為非空檔案不可能匹配成功
RS="\n+":按行讀取,但忽略所有空行
所以在讀取資料前,可以改變RS來改變每次讀取資料的模式
1.2、awk的執行過程
- 自動從指定的資料檔案中讀取一個資料行.
- 自動更新(Update)相關的內建變數之值. 如 : NF, NR, $0...
- 依次執行程式中 所有 的 Pattern { Actions } 指令.
- 當執行完程式中所有 Pattern { Actions } 時, 若資料檔案中還有未讀取的資料, 則反覆執行步驟1到步驟4.
1.3、本文例項資料
ID name gender age email phone
1 zhangs female 34 aaa@163.com 13423394013
2 lisi male 45 abb@gmail.com 15608492523
3 wmzi female 61 ccc@sohu.com 18848796505
4 wangw female 31 acc@sohu.com 18848796506
5 liub male 56 gdd@163.com 18848796507
6 zhangf female 34 adb@139.com 18848796573
7 guany male 56 eab@189.com 17048796508
8 zhaoy female 35 189@163.com 17058796503
9 huangz male 65 dd@189.com 13648796593
10 lvbu male 43 byy@sohu.com 18148796803
11 sunwk male 99 suk@sohu.com 18148706803
二、awk使用語法和選項
2.1、使用語法
awk [ -- ] program-text file ... (1)
awk -f program-file [ -- ] file ... (2)
awk -e program-text [ -- ] file ... (3)
program-txt是awk命令列中的awk程式碼,一般使用單引號包圍
-f program-file表示將awk程式碼寫在檔案中,然後使用-f選項去執行該檔案
-e program-text也用於指定awk程式碼,如果要結合檔案和命令列一起使用,必須使用-e和-f,不能使用(1)
2.2、使用選項
-e program-text
--source program-text
指定awk程式表示式,可結合-f選項同時使用
在使用了-f選項後,如果不使用-e,awk program是不會執行的,它會被當作ARGV的一個引數
-f program-file
--file program-file
從檔案中讀取awk原始碼來執行,可指定多個-f選項
-F fs
--field-separator fs
指定輸入欄位分隔符(FS預定義變數也可設定)
-n
--non-decimal-data
識別檔案輸入中的8進位制數(0開頭)和16進位制數(0x開頭)
echo '030' | awk -n '{print $1+0}'
-o [filename]
格式化awk程式碼。
不指定filename時,則預設儲存到awkprof.out
指定為`-`時,表示輸出到標準輸出
-v var=val
--assign var=val
在BEGIN之前,宣告並賦值變數var,變數可在BEGIN中使用
2.3、awk程式語法
awk程式中主要語法是 Pattern { Actions}
Pattern:為匹配模式,即判定某一行是否符合該模式,然後才執行Actions
Actions:為執行邏輯,其中多個action如果寫在同一行,則需使用分號分隔
pattern和action都可以省略
- 省略pattern,等價於對每一行資料都執行action
- 例如:awk '{print $0}' test.txt
省略程式碼塊{action},等價於{print}即輸出所有行
- 例如:awk '/Alice/' test.txt等價於awk '/Alice/{print $0}' test.txt
- 省略程式碼塊中的action,表示對篩選的行什麼都不做
- 例如:awk '/Alice/{}' test.txt
pattern{action}任何一部分都可以省略
- 例如:awk '' test.txt
pattern中使用邏輯與、非、或
pattern && pattern # 邏輯與 3>2 && 3>1 {action}
pattern || pattern # 邏輯或 3>2 || 3<1 {action}
! pattern # 邏輯取反 !/a.*ef/{action}
常用的Pattern
# 1.根據行號篩選
NR==2 # 篩選出第二行
NR>=2 # 輸出第2行和之後的行
# 2.根據正規表示式篩選整行
/qq.com/ # 輸出帶有qq.com的行
$0 ~ /qq.com/ # 等價於上面命令
/^[^@]+$/ # 輸出不包含@符號的行
!/@/ # 輸出不包含@符號的行
# 3.根據欄位來篩選行
($4+0) > 24{print $0} # 輸出第4欄位大於24的行
$5 ~ /qq.com/ # 輸出第5欄位包含qq.com的行
# 4.將多個篩選條件結合起來進行篩選
NR>=2 && NR<=7 #輸出行數大於等於2並且小於等於7
$3=="male" && $6 ~ /^170/ #輸出第三個欄位等於male並且第六個欄位不用170開頭
$3=="male" || $6 ~ /^170/ #輸出第三個欄位等於male或者第六個欄位不用170開頭
# 5.按照範圍進行篩選 flip flop
# pattern1,pattern2{action}
NR==2,NR==7 # 輸出第2到第7行
NR==2,$6 ~ /^170/ # 輸出第2行到匹配第六個欄位170開頭的行
action有很多,例如:
awk的 I/O指令 : print, printf( ), getline...
awk的 流程控制指令 : if(...){..} else{..}, while(...){...}...
例項:
[root@lgh test]# awk '' test.txt #action和pattern都省了
[root@lgh test]# awk '/163/{print $0}' test.txt #匹配行中有163的行,並輸出
1 zhangs female 34 aaa@163.com 13423394013
5 liub male 56 gdd@163.com 18848796507
8 zhaoy female 35 189@163.com 17058796503
[root@lgh test]# awk '/163/' test.txt #省略action,預設輸出$0
1 zhangs female 34 aaa@163.com 13423394013
5 liub male 56 gdd@163.com 18848796507
8 zhaoy female 35 189@163.com 17058796503
[root@lgh test]# awk '{print $0}' test.txt #省略pattern,輸出全部行
ID name gender age email phone
1 zhangs female 34 aaa@163.com 13423394013
2 lisi male 45 abb@gmail.com 15608492523
3 wmzi female 61 ccc@sohu.com 18848796505
4 wangw female 31 acc@sohu.com 18848796506
5 liub male 56 gdd@163.com 18848796507
6 zhangf female 34 adb@139.com 18848796573
7 guany male 56 eab@189.com 17048796508
8 zhaoy female 35 189@163.com 17058796503
9 huangz male 65 dd@189.com 13648796593
10 lvbu male 43 byy@sohu.com 18148796803
11 sunwk male 99 suk@sohu.com 18148706803
三、awk內建變數
預定義變數分為兩類:控制awk工作的變數和攜帶資訊的變數
第一類:控制AWK工作的預定義變數
$0,$1,$2,...$NF:其中$0表示整行資料,$1,$2,...,$NF分別表示該行的第一個欄位、第二個欄位,...,最後一個欄位
RS:輸入記錄分隔符,預設為換行符\n
IGNORECASE:預設值為0,表示所有的正則匹配不忽略大小寫。設定為非0值(例如1),之後的匹配將忽略大小寫。例如在BEGIN塊中將其設定為1,將使FS、RS都以忽略大小寫的方式分隔欄位或分隔record
FS:讀取記錄後,劃分為欄位的欄位分隔符。
FIELDWIDTHS:以指定寬度切割欄位而非按照FS。
FPAT:以正則匹配匹配到的結果作為欄位,而非按照FS劃分。
OFS:print命令輸出各欄位列表時的輸出欄位分隔符,預設為空格" "
ORS:print命令輸出資料時在尾部自動新增的記錄分隔符,預設為換行符\n
CONVFMT:在awk中數值隱式轉換為字串時,將根據CONVFMT的格式按照sprintf()的方式自動轉換為字串。預設值為”%.6g
OFMT:在print中,數值會根據OFMT的格式按照sprintf()的方式自動轉換為字串。預設值為”%.6g
第二類:攜帶資訊的預定義變數
ARGC和ARGV:awk命令列引數的數量、命令引數的陣列。
ARGIND:awk當前正在處理的檔案在ARGV中的索引位置。所以,如果awk正在處理命令列引數中的某檔案,則ARGV[ARGIND] == FILENAME為真
FILENAME:awk當前正在處理的檔案(命令列中指定的檔案),所以在BEGIN中該變數值為空
ENVIRON:儲存了Shell的環境變數的陣列。例如ENVIRON["HOME"]將返回當前使用者的家目錄
NR:當前已讀總記錄數,多個檔案從不會重置為0,所以它是一直疊加的,可以直接修改NR,下次讀取記錄時將在此修改值上自增
FNR:當前正在讀取檔案的第幾條記錄,每次開啟新檔案會重置為0,可以直接修改FNR,下次讀取記錄時將在此修改值上自增
NF:當前記錄的欄位數,
RT:在讀取記錄時真正的記錄分隔符,
RLENGTH:match()函式正則匹配成功時,所匹配到的字串長度,如果匹配失敗,該變數值為-1
RSTART:match()函式匹配成功時,其首字元的索引位置,如果匹配失敗,該變數值為0
SUBSEP:arr[x,y]中下標分隔符構建成索引時對應的字元,預設值為\034,是一個不太可能出現在字串中的不可列印字元。
1、$0,$1,$2,...,$NF
[root@lgh test]# awk '{print $0,$1,$2,$NF}' test.txt #輸出整行,然後輸出第一,第二,和最後一個欄位的值
ID name gender age email phone ID name phone
1 zhangs female 34 aaa@163.com 13423394013 1 zhangs 13423394013
2 lisi male 45 abb@gmail.com 15608492523 2 lisi 15608492523
3 wmzi female 61 ccc@sohu.com 18848796505 3 wmzi 18848796505
4 wangw female 31 acc@sohu.com 18848796506 4 wangw 18848796506
5 liub male 56 gdd@163.com 18848796507 5 liub 18848796507
6 zhangf female 34 adb@139.com 18848796573 6 zhangf 18848796573
7 guany male 56 eab@189.com 17048796508 7 guany 17048796508
8 zhaoy female 35 189@163.com 17058796503 8 zhaoy 17058796503
9 huangz male 65 dd@189.com 13648796593 9 huangz 13648796593
10 lvbu male 43 byy@sohu.com 18148796803 10 lvbu 18148796803
11 sunwk male 99 suk@sohu.com 18148706803 11 sunwk 18148706803
2、RS、IGNORECASE
RS有兩種情況:
RS為單個字元:直接使用該字元作為分隔符進行分割記錄
RS為多個字元:將其當做正規表示式,只要匹配正規表示式的符號,都用來做分割記錄,設定預定義變數INGORECASE為非零值,表示正則忽略大小寫
[root@lgh test]# echo "bababaABAB" | awk 'BEGIN{IGNORECASE=1;RS="a+"}{print $0}' #正則忽略大小寫切分讀取
b
b
b
B
B
[root@lgh test]# echo "bababaABAB" | awk 'BEGIN{IGNORECASE=0;RS="a+"}{print $0}' #正則不忽略大小寫切分讀取
b
b
b
ABAB
[root@lgh test]# echo "bababaABAB" | awk 'BEGIN{IGNORECASE=1;RS="a"}{print $0}' #非正則,IGNORECASE無效
b
b
b
ABAB
3、分隔符FS、FIELDWIDTHS
- FS為單個字元時,該字元即為欄位分隔符
- FS為多個字元時,則採用正規表示式模式作為欄位分隔符
- 特殊的,也是FS預設的情況,FS為單個空格時,將以連續的空白(空格、製表符、換行符)作為欄位分隔符
- 特殊的,FS為空字串””時,將對每個字元都進行分隔,即每個字元都作為一個欄位
- 設定預定義變數IGNORECASE為非零值,正則匹配時表示忽略大小寫(隻影響正則,所以FS為單字時無影響)
[root@lgh test]# awk -F" +|@" '{print $1,$2,$3,$4,$5,$6,$7}' test.txt #正則,一個空格或者多個空格,或者@符進行分割
ID name gender age email phone
1 zhangs female 34 aaa 163.com 13423394013
2 lisi male 45 abb gmail.com 15608492523
3 wmzi female 61 ccc sohu.com 18848796505
4 wangw female 31 acc sohu.com 18848796506
5 liub male 56 gdd 163.com 18848796507
6 zhangf female 34 adb 139.com 18848796573
7 guany male 56 eab 189.com 17048796508
8 zhaoy female 35 189 163.com 17058796503
9 huangz male 65 dd 189.com 13648796593
10 lvbu male 43 byy sohu.com 18148796803
11 sunwk male 99 suk sohu.com 18148706803
[root@lgh test]# awk '{print $1,$2}' FS=":" /etc/passwd | head -2 #使用FS指定:為分割分
root x
bin x
[root@lgh test]# awk -F":" '{print $1,$2}' /etc/passwd | head -2 #使用-F指定:分隔符
root x
bin x
預定義變數FIELDWIDTHS按字元寬度分割欄位
FIELDWIDTHS="1 2 3 4" :表示第一個欄位1個字元,第二個2個字元,第三個3個字元,第四個4個字元
[root@lgh test]# cat name.txt #檔案內容
ID name gender age email phone
1 female 34 aaa@163.com 13423394013
2 lisi male 45 abb@gmail.com 15608492523
[root@lgh test]# awk '{print $2}' name.txt #獲取第二列,其中female是第三列的內容
name
female
lisi
[root@lgh test]# awk 'BEGIN{FIELDWIDTHS="8 8"}{print $2}' name.txt #正確的獲取第二列
name
lisi
[root@lgh test]# echo 'bababaABAB' | awk 'BEGIN{FIELDWIDTHS="1 2 3 4"}{print $1,$2,$3,$4}' #使用指定字元長度分割
b ab aba ABAB
4、FS、OFS
FS是讀取行時的欄位分割,OFS是輸出時的欄位分隔符
[root@lgh test]# awk 'BEGIN{OFS="="}{print $1,$2}' test.txt
ID=name
1=zhangs
2=lisi
3=wmzi
4=wangw
5=liub
6=zhangf
7=guany
8=zhaoy
9=huangz
10=lvbu
11=sunwk
[root@lgh test]# awk 'BEGIN{OFS="-"}{$1=$1;print $0}' test.txt #重組$0,修改$1,$2,...,$NF中的內容,以及增大或者減少NF的值,都會產生$0的重組
ID-name-gender-age-email-phone
1-zhangs-female-34-aaa@163.com-13423394013
2-lisi-male-45-abb@gmail.com-15608492523
3-wmzi-female-61-ccc@sohu.com-18848796505
4-wangw-female-31-acc@sohu.com-18848796506
5-liub-male-56-gdd@163.com-18848796507
6-zhangf-female-34-adb@139.com-18848796573
7-guany-male-56-eab@189.com-17048796508
8-zhaoy-female-35-189@163.com-17058796503
9-huangz-male-65-dd@189.com-13648796593
10-lvbu-male-43-byy@sohu.com-18148796803
11-sunwk-male-99-suk@sohu.com-18148706803
5、RS、ORS
RS是讀取時的行分割,ORS是輸出時的行分割
[root@lgh test]# echo -e "a\nb" |awk '{print $1}' #RS預設\n
a
b
[root@lgh test]# echo -e "a\nb" |awk 'BEGIN{ORS="hello word"}{print $1}' #使用hello word代替\n進行輸出
ahello wordbhello word
6、OFMT
變數OFMT(Output format)定義的格式按照sprintf()相同的方式進行格式化。OFMT預設值為%.6g,表示有效位(整數部分加小數部分)最多為6。
[root@lgh test]# awk 'BEGIN{OFMT="%.3f";print 3.141592678}' #會進行四捨五入
3.142
[root@lgh test]# awk 'BEGIN{print 3.141592678}'
3.14159
常用格式符:
%c :將ASCII碼轉換為字元,
%d :轉為整數
%e :科學計數方法輸出
%o :轉為8進位制輸出
%s :轉為字串
%x :轉16進位制
7、NR、FNR
NR是該程式讀取的記錄數,FNR是程式讀取某個檔案的的記錄數
[root@lgh test]# awk '{print NR,FNR,$0}' test.txt test.txt #NR一直遞增,FNR根據檔案重新設定1,然後遞增
1 1 ID name gender age email phone
2 2 1 zhangs female 34 aaa@163.com 13423394013
3 3 2 lisi male 45 abb@gmail.com 15608492523
4 4 3 wmzi female 61 ccc@sohu.com 18848796505
5 5 4 wangw female 31 acc@sohu.com 18848796506
6 6 5 liub male 56 gdd@163.com 18848796507
7 7 6 zhangf female 34 adb@139.com 18848796573
8 8 7 guany male 56 eab@189.com 17048796508
9 9 8 zhaoy female 35 189@163.com 17058796503
10 10 9 huangz male 65 dd@189.com 13648796593
11 11 10 lvbu male 43 byy@sohu.com 18148796803
12 12 11 sunwk male 99 suk@sohu.com 18148706803
13 1 ID name gender age email phone
14 2 1 zhangs female 34 aaa@163.com 13423394013
15 3 2 lisi male 45 abb@gmail.com 15608492523
16 4 3 wmzi female 61 ccc@sohu.com 18848796505
17 5 4 wangw female 31 acc@sohu.com 18848796506
18 6 5 liub male 56 gdd@163.com 18848796507
19 7 6 zhangf female 34 adb@139.com 18848796573
20 8 7 guany male 56 eab@189.com 17048796508
21 9 8 zhaoy female 35 189@163.com 17058796503
22 10 9 huangz male 65 dd@189.com 13648796593
23 11 10 lvbu male 43 byy@sohu.com 18148796803
24 12 11 sunwk male 99 suk@sohu.com 18148706803
8、ARGS、ARGV
預定義變數ARGV是一個陣列,包含了所有的命令列引數。該陣列使用從0開始的數值作為索引。
預定義變數ARGC初始時是ARGV陣列的長度,即命令列引數的數量。
ARGV陣列的數量和ARGC的值只有在awk剛開始執行的時候是保證相等的。
[root@lgh test]# awk 'BEGIN{print ARGC;for(i in ARGV){print "ARGV[" i "]= " ARGV[i]}}' a b c
4
ARGV[0]= awk
ARGV[1]= a
ARGV[2]= b
ARGV[3]= c
四、BEGIN和END
awk的所有程式碼(目前這麼認為)都是寫在語句塊中的。
每個語句塊前面可以有pattern,比如:pattern1{action1}pattern2{action3;action4;...}
語句塊可分為3類:BEGIN語句塊、END語句塊和main語句塊。其中BEGIN語句塊和END語句塊都是的格式分別為BEGIN{...}和END{...},而main語句塊是一種統稱,它的pattern部分沒有固定格式,也可以省略,main程式碼塊是在讀取檔案的每一行的時候都執行的程式碼塊。
BEGIN程式碼塊:
在讀取檔案之前執行,且執行一次
在BEGIN程式碼塊中,無法使用$0或其它一些特殊變數
main程式碼塊:
讀取檔案時迴圈執行,(預設情況)每讀取一行,就執行一次main程式碼塊
main程式碼塊可有多個
END程式碼塊:
在讀取檔案完成之後執行,且執行一次
有END程式碼塊,必有要讀取的資料(可以是標準輸入)
END程式碼塊中可以使用$0等一些特殊變數,只不過這些特殊變數儲存的是最後一輪awk迴圈的資料
[root@lgh test]# awk 'BEGIN{print "hello word"}/^1/{print $0}END{print "END hello word"}' test.txt
hello word
1 zhangs female 34 aaa@163.com 13423394013
10 lvbu male 43 byy@sohu.com 18148796803
11 sunwk male 99 suk@sohu.com 18148706803
END hello word
例項如上:begin和end均只輸出一次,main程式碼中匹配1開頭的行並輸出
五、運算子和邏輯運算子
5.1、運算子
$ # $(2+2)
++ --
^ **
+ - ! # 一元運算子
* / %
+ -
space # 這是字元連線操作 `12 " " 23` `12 " " -23`
| |&
< > <= >= != == # 注意>即是大於號,也是print/printf的重定向符號
~ !~
in
&&
||
?:
= += -= *= /= %= ^=
5.2、邏輯運算子
&& 邏輯與
|| 邏輯或
! 邏輯取反
expr1 && expr2 # 如果expr1為假,則不用計算expr2
expr1 || expr2 # 如果expr1為真,則不用計算expr2
# 注:
# 1. && ||會短路運算
# 2. !優先順序高於&&和||
# 所以`! expr1 && expr2`等價於`(! expr1) && expr2`
六、輸出和重定向
輸出格式:print elem1,elem2,elem3...
逗號分隔要列印的欄位列表,各欄位都會自動轉換成字串格式,然後通過預定義變數OFS值(其預設值為空格)連線各欄位進行輸出
在print輸出時會自動在尾部加上輸出記錄分隔符,輸出記錄分隔符的預定義變數為ORS,其預設值為\n
sprintf()採用和printf相同的方式格式化字串,但是它不會輸出格式化後的字串,而是返回格式化後的字串。所以,可以將格式化後的字串賦值給某個變數。
[root@lgh test]# awk 'BEGIN{var=sprintf("%.3f",3.141592678);print var}' #使用sprintf進行格式化複製變數,然後輸出
3.142
[root@lgh test]# awk 'BEGIN{var=sprintf("%d",3.141592678);print var}'
3
[root@lgh test]# awk 'BEGIN{OFMT="%.3f";print 3.141592678}' #使用OFMT指定格式進行輸出
3.142
[root@lgh test]# awk 'BEGIN{OFMT="%d";print 3.141592678}'
3
重定向:
>filename時,如果檔案不存在,則建立,如果檔案存在則首先截斷。之後再輸出到該檔案時將不再截斷。
>>filename時,將追加資料,檔案不存在時則建立。
awk中只要不close(),任何檔案都只會在第一次使用時開啟,之後都不會再重新開啟。
[root@lgh test]# awk '{print $1,$2 > "name.txt"}' test.txt #重定向到name.txt檔案
[root@lgh test]# cat name.txt
ID name
1 zhangs
2 lisi
3 wmzi
4 wangw
5 liub
6 zhangf
7 guany
8 zhaoy
9 huangz
10 lvbu
11 sunwk
七、陣列
awk陣列特性:
- awk的陣列是關聯陣列(即key/value方式的hash資料結構),索引下標可為數值(甚至是負數、小數等),也可為字串
- 在內部,awk陣列的索引全都是字串,即使是數值索引在使用時內部也會轉換成字串
- awk的陣列元素的順序和元素插入時的順序很可能是不相同的
- awk陣列支援陣列的陣列
- 通過索引的方式訪問陣列中不存在的元素時,會返回空字串,同時會建立這個元素並將其值設定為空字串。
陣列的訪問和賦值都是通過制定下標就行賦值和訪問,下標可以為整數,負數,字串,小數
例如:
[root@lgh test]# cat a.awk
{
arr[i]="a"
arr[-1]="b"
print arr[i]
print arr[-1]
print length(arr)
print arr[1] #輸出不存在的陣列下標內容
print "################"
print length(arr) #長度+1
}
[root@lgh test]# echo 0 | awk -f a.awk
a
b
2
################
3
陣列遍歷、刪除、判讀某key是否在arr中:
[root@lgh test]# cat a.awk
{
arr["x"]="a"
arr[-1]="b"
arr[4.5]=1
arr[4]=5
for(idx in arr){ #遍歷
print arr[idx]
}
print "###############"
delete arr[4] #刪除元素,也可以刪除整個陣列delete arr
for(idx in arr){ #遍歷無序,與建立陣列時輸入的元素順序不一定一致
print idx,arr[idx]
}
print "##############"
print (-1 in arr) #判斷key=-1,是否在陣列中,是返回1,不是返回0
print (99 in arr)
}
[root@lgh test]# echo 0 | awk -f a.awk
5
b
a
1
###############
-1 b
x a
4.5 1
##############
1
0
八、流程控制
8.1、判斷、選擇
if判斷:
if(condition){statement }
if(condition){statement }else{statement }
if(condition){statement }else if(condition){statement }else{statement }
三目運算
expr1 ? expr2 : expr3
switch選擇:
switch (expression) {
case value1|regex1 : statements1
case value2|regex2 : statements2
case value3|regex3 : statements3
...
[ default: statement ]
}
[root@lgh test]# awk 'NR>1{$1>5? var="B": var="A";print $1,var}' test.txt
1 A
2 A
3 A
4 A
5 A
6 B
7 B
8 B
9 B
10 B
11 B
[root@lgh test]# awk 'NR>1{if($1<5){print $1,"A"}else{print $1,"B"}}' test.txt
1 A
2 A
3 A
4 A
5 B
6 B
7 B
8 B
9 B
10 B
11 B
8.2、迴圈
while和do…while
while(condition){
statements
}
do {
statements
} while(condition)
for迴圈
for (expr1; expr2; expr3) {
statement
}
for (idx in array) {
statement
}
awk '{i=1;while(i<=NF){print $i;i++}}' test.txt
awk '{for(i=1;i<=NF;i++) print $i}' test.txt
8.3、關鍵字
break:break可退出for、while、do…while、switch語句。
continue:continue可讓for、while、do…while進入下一輪迴圈。
next:next會在當前語句處立即停止後續操作,並讀取下一行,進入迴圈頂部。
nextfile:nextfile會在當前語句處立即停止後續操作,並直接讀取下一個檔案,並進入迴圈頂部。
exit:直接退出awk程式,注意,END語句塊也是exit操作的一部分,所以在BEGIN或main段中執行exit操作,也會執行END語句塊。如果真的想直接退出整個awk,則可以先設定一個flag變數,然後在END語句塊的開頭檢查這個變數再exit。
[root@lgh test]# awk 'NR==3{next}{print $0}' test.txt #匹配到了第3行,然後跳過
ID name gender age email phone
1 zhangs female 34 aaa@163.com 13423394013
3 wmzi female 61 ccc@sohu.com 18848796505
4 wangw female 31 acc@sohu.com 18848796506
5 liub male 56 gdd@163.com 18848796507
6 zhangf female 34 adb@139.com 18848796573
7 guany male 56 eab@189.com 17048796508
8 zhaoy female 35 189@163.com 17058796503
9 huangz male 65 dd@189.com 13648796593
10 lvbu male 43 byy@sohu.com 18148796803
11 sunwk male 99 suk@sohu.com 18148706803
[root@lgh test]# awk 'NR==3{nextfile}{print $0}' test.txt test.txt #匹配到第3行,跳過該檔案,執行下一個檔案
ID name gender age email phone
1 zhangs female 34 aaa@163.com 13423394013
ID name gender age email phone
1 zhangs female 34 aaa@163.com 13423394013
2 lisi male 45 abb@gmail.com 15608492523
3 wmzi female 61 ccc@sohu.com 18848796505
4 wangw female 31 acc@sohu.com 18848796506
5 liub male 56 gdd@163.com 18848796507
6 zhangf female 34 adb@139.com 18848796573
7 guany male 56 eab@189.com 17048796508
8 zhaoy female 35 189@163.com 17058796503
9 huangz male 65 dd@189.com 13648796593
10 lvbu male 43 byy@sohu.com 18148796803
11 sunwk male 99 suk@sohu.com 18148706803
[root@lgh test]# awk 'BEGIN{ print "hello work";exit}END{print "god job"}' #預設會執行END中的程式碼
hello work
god job
[root@lgh test]# awk 'BEGIN{ print "hello work";;exit flag=1}END{if(flag) exit ;print "god job"}' #使用flag跳出
hello work
九、變數
awk中語句塊沒有作用域,都是全域性變數(除非是函式中的形參)
變數的賦值:
可以x=y=z=5,等價於z=5 y=5 x=5
可以將賦值語句放在任意允許使用表示式的地方
x != (y = 1)
awk 'BEGIN{print (a=4);print a}'
awk中宣告變數的位置:
在BEGIN或main或END程式碼段中直接引用或賦值
使用-v var=val選項,可定義多個,必須放在awk程式碼的前面
它的變數宣告早於BEGIN塊
普通變數:awk -v age=123 'BEGIN{print age}'
使用shell變數賦值:awk -v age=$age 'BEGIN{print age}'
在awk程式碼後面使用var=val引數
它的變數宣告在BEGIN之後
awk '{print n}' n=3 a.txt n=4 b.txt
awk '{print $1}' FS=' ' a.txt FS=":" /etc/passwd
使用Shell變數賦值:awk '{print age}' age=$age a.txt
十、函式和自定義函式
10.1、自定義函式
使用function關鍵字來定義函式:
function func_name([parameters]){
function_body
}
函式可以定義的位置:
awk '_ BEGIN{} _ MAIN{} _ END{} _' #可以定義在任何下劃線的地方
[root@lgh test]# cat a.awk
BEGIN{
f()
}
function f(){
print "自定義函式"
}
{}#main
END{}
[root@lgh test]# echo 0 | awk -f a.awk
自定義函式
[root@lgh test]# vim a.awk
[root@lgh test]# cat a.awk
function f(){
print "自定義函式"
}
BEGIN{
f()
}
{}#main
END{}
[root@lgh test]# echo 0 | awk -f a.awk
自定義函式
[root@lgh test]# vim a.awk
[root@lgh test]# cat a.awk
BEGIN{
f()
}
{}#main
END{}
function f(){
print "自定義函式"
}
[root@lgh test]# echo 0 | awk -f a.awk
自定義函式
帶引數和return的自定義函式:
[root@lgh test]# cat a.awk
BEGIN{
print f(1,2)
}
{}#main
END{}
function f(a,b){
print "自定義函式"
return a+b
}
[root@lgh test]# echo 0 | awk -f a.awk
自定義函式
3
引數的傳遞形式:
傳遞普通變數時,是按值拷貝傳遞
- 直接拷貝普通變數的值到函式中
- 函式內部修改不會影響到外部
傳遞陣列時,是按引用傳遞
- 函式內部修改會影響到外部
awk變數作用域:
- awk只有在函式引數中才是區域性變數,其它地方定義的變數均為全域性變數。
- 函式內部新增的變數是全域性變數,會影響到全域性,所以在函式退出後仍然能訪問
- 函式引數會遮掩全域性同名變數,所以在函式執行時,無法訪問到或操作與引數同名的全域性變數,函式退出時會自動撤掉遮掩,這時才能訪問全域性變數。所以,引數具有區域性效果。
[root@lgh test]# cat a.awk
BEGIN{
a=50 #全域性
print a #50
print f(a,2) #42
print a #50
}
{}#main
END{}
function f(a,b){
print "自定義函式"
a=40 #覆蓋了全域性變數a
print "區域性",a
return a+b #40+2
}
[root@lgh test]# echo 0 | awk -f a.awk
50
自定義函式
區域性 40
42
50
10.2、getline函式
getline函式用於從檔案、標準輸入或管道中讀取資料,並按情況設定變數的值。getline可以自動不斷的載入下一行。如果能讀取記錄,則getline的返回值為1,遇到輸入流的尾部時,返回值為0,不能讀取記錄(如檔案沒有讀取許可權、檔案不存在)時,返回值為“-1"。
其中:
- getline:會從主輸入檔案中讀取記錄。會同時設定$0,NF,NR,FNR。
- getline var:會從主輸入檔案中讀取記錄,並將讀取的記錄賦值給變數var。會同時設定var,NR,FNR。
- getline <file:從外部檔案file中讀取記錄。同時會設定$0,NF。
- getline var <file:從外部檔案file中讀取記錄,並將讀取的記錄賦值給變數var。會同時設定var。
- cmd | getline:從管道中讀取記錄。會同時設定$0,NF。
- cmd | getline var:從管道中讀取記錄,並將讀取的記錄賦值給變數var。會同時設定var。
getline與netx的區別
getline:讀取下一行之後,繼續執行getline後面的程式碼
next:讀取下一行,立即回頭awk迴圈的頭部,不會再執行next後面的程式碼
[root@lgh test]# awk '/^1/{print;getline;print}' test.txt #匹配1開頭的行,在1開頭的時候,獲取到了下一行,也列印了出來
1 zhangs female 34 aaa@163.com 13423394013
2 lisi male 45 abb@gmail.com 15608492523
10 lvbu male 43 byy@sohu.com 18148796803
11 sunwk male 99 suk@sohu.com 18148706803
[root@lgh test]# awk '/^1/{print;getline;print;exit}' test.txt #執行了getline,print,然後exit了
1 zhangs female 34 aaa@163.com 13423394013
2 lisi male 45 abb@gmail.com 15608492523
[root@lgh test]# awk '/^1/{print;if((getline var)<0)exit;print var}' test.txt #getline賦值給了var
1 zhangs female 34 aaa@163.com 13423394013
2 lisi male 45 abb@gmail.com 15608492523
10 lvbu male 43 byy@sohu.com 18148796803
11 sunwk male 99 suk@sohu.com 18148706803
[root@lgh test]# awk '/^1/{print;if((getline var)<0)exit;print $0}' test.txt #getline賦值給了var,但是沒有設定$0
1 zhangs female 34 aaa@163.com 13423394013
1 zhangs female 34 aaa@163.com 13423394013
10 lvbu male 43 byy@sohu.com 18148796803
10 lvbu male 43 byy@sohu.com 18148796803
[root@lgh test]# awk '/^2/{while((getline var <"a.awk")>0)print var;close("a.awk")}' test.txt #匹配2開頭的行,只有一行,所以後面只執行一次,把a.awk裡面的內容賦值給var變數,然後輸出
BEGIN{
a=50
print a
print f(a,2)
print a
}
{}#main
END{}
function f(a,b){
print "自定義函式"
a=40
print "區域性",a
return a+b
}
[root@lgh test]# awk '/^2/{while((getline<"a.awk")>0){print};close("a.awk")}' test.txt #匹配2開頭的行,只有一行,所以只執行一次,輸出a.awk中的內容
BEGIN{
a=50
print a
print f(a,2)
print a
}
{}#main
END{}
function f(a,b){
print "自定義函式"
a=40
print "區域性",a
return a+b
}
[root@lgh test]# awk '/^2/{"date"|getline;print}' test.txt #使用linux的date命令,通過管道,使用getline讀取
Tue Nov 24 19:44:28 CST 2020
[root@lgh test]# awk '/^2/{"date"|getline var ;print var}' test.txt
Tue Nov 24 19:44:41 CST 2020
[root@lgh test]# awk '/^1/{"date"|getline var ;print var}' test.txt
Tue Nov 24 19:44:48 CST 2020
Tue Nov 24 19:44:48 CST 2020
Tue Nov 24 19:44:48 CST 2020
awk雖然強大,但是有些資料仍然不方便處理,這時可將資料交給Shell命令去幫助處理,然後再從Shell命令的執行結果中取回處理後的資料繼續awk處理。awk通過|&符號來支援coproc。
awk_print[f] "something" |& Shell_Cmd
Shell_Cmd |& getline [var]
這表示awk通過print輸出的資料將傳遞給Shell的命令Shell_Cmd去執行,然後awk再從Shell_Cmd的執行結果中取回Shell_Cmd產生的資料
[root@lgh test]# cat a.awk
BEGIN{
CMD="sed -nr \"s/.*@(.*)$/\\1/p\""; #獲取郵箱@符號後面的內容
}
NR>1{
print $5; #輸出郵箱名
print $5 |& CMD; #擷取郵箱@符號後面的內容
close(CMD,"to");
CMD |& getline email_domain; #把郵箱@後面的內容賦值給變數email_domain
close(CMD);
print email_domain; #輸出email_domain
}
[root@lgh test]# awk -f a.awk test.txt
aaa@163.com
163.com
abb@gmail.com
gmail.com
ccc@sohu.com
sohu.com
acc@sohu.com
sohu.com
gdd@163.com
163.com
adb@139.com
139.com
eab@189.com
189.com
189@163.com
163.com
dd@189.com
189.com
byy@sohu.com
sohu.com
suk@sohu.com
sohu.com
10.3、內建字串函式
index(str1,str2):返回子串str2在字串str1中第一次出現的位置。如果沒有指定str1,則返回0。
length(str1):返回字串str1的長度。如果未給定str1,則表示計算"$0"的長度。
substr(str1,p):返回str1中從p位置開始的字尾字串。
substr(str1,p,n):返回str1中從p位置開始,長度為n的子串。
match(str1,regexp):如果regexp能匹配str1,則返回匹配起始位置。否則返回0。它會設定內建變數RSTART和RLENGTH的值。
split(str1,array,sep):使用欄位分隔符sep將str1分割到陣列array中,並返回陣列的元素個數。如果未指定sep則採用FS的值。因此該函式用於切分欄位到陣列中,下標從1開始。
sprintf(fmt,expr):根據printf的格式fmt,返回格式化後的expr。
sub(regexp,rep,str2):將str2中第一個被regexp匹配的字串替換成rep,替換成功則返回1(表示替換了1次),否則返回0。注意是貪婪匹配。
sub(regexp,rep):將"$0"中第一個被regexp匹配的字串替換成rep,替換成功則返回1,否則返回0。注意是貪婪匹配。
gsub(regexp,rep,str2):將str2中所有被regexp匹配的內容替換成rep,並返回替換的次數。
gsub(regexp,rep):將"$0"中所有被regexp匹配的內容替換成rep,並返回替換的次數。
toupper(str):將str轉換成大寫字母,並返回新串。
tolower(str):將str轉換成小寫字母,並返回新串。
10.4、內建數值函式
int(expr) 截斷為整數:int(123.45)和int("123abc")都返回123,int("a123")返回0
sqrt(expr) 返回平方根
rand() 返回[0,1)之間的隨機數,預設使用srand(1)作為種子值
srand([expr]) 設定rand()種子值,省略引數時將取當前時間的epoch值(精確到秒的epoch)作為種子值
參考:
https://www.cnblogs.com/f-ck-need-u/p/7509812.html#9-awk-