"三劍客"之awk心中無劍
一、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 [email protected] 13423394013 2 lisi male 45 [email protected] 15608492523 3 wmzi female 61 [email protected] 18848796505 4 wangw female 31 [email protected] 18848796506 5 liub male 56 [email protected] 18848796507 6 zhangf female 34 [email protected] 18848796573 7 guany male 56 [email protected] 17048796508 8 zhaoy female 35 [email protected] 17058796503 9 huangz male 65 [email protected] 13648796593 10 lvbu male 43 [email protected] 18148796803 11 sunwk male 99 [email protected] 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 [email protected] 13423394013 5 liub male 56 [email protected] 18848796507 8 zhaoy female 35 [email protected] 17058796503 [root@lgh test]# awk '/163/' test.txt #省略action,預設輸出$0 1 zhangs female 34 [email protected] 13423394013 5 liub male 56 [email protected] 18848796507 8 zhaoy female 35 [email protected] 17058796503 [root@lgh test]# awk '{print $0}' test.txt #省略pattern,輸出全部行 ID name gender age email phone 1 zhangs female 34 [email protected] 13423394013 2 lisi male 45 [email protected] 15608492523 3 wmzi female 61 [email protected] 18848796505 4 wangw female 31 [email protected] 18848796506 5 liub male 56 [email protected] 18848796507 6 zhangf female 34 [email protected] 18848796573 7 guany male 56 [email protected] 17048796508 8 zhaoy female 35 [email protected] 17058796503 9 huangz male 65 [email protected] 13648796593 10 lvbu male 43 [email protected] 18148796803 11 sunwk male 99 [email protected] 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 [email protected] 13423394013 1 zhangs 13423394013 2 lisi male 45 [email protected] 15608492523 2 lisi 15608492523 3 wmzi female 61 [email protected] 18848796505 3 wmzi 18848796505 4 wangw female 31 [email protected] 18848796506 4 wangw 18848796506 5 liub male 56 [email protected] 18848796507 5 liub 18848796507 6 zhangf female 34 [email protected] 18848796573 6 zhangf 18848796573 7 guany male 56 [email protected] 17048796508 7 guany 17048796508 8 zhaoy female 35 [email protected] 17058796503 8 zhaoy 17058796503 9 huangz male 65 [email protected] 13648796593 9 huangz 13648796593 10 lvbu male 43 [email protected] 18148796803 10 lvbu 18148796803 11 sunwk male 99 [email protected] 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 [email protected] 13423394013 2 lisi male 45 [email protected] 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 [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected]
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 [email protected] 13423394013 3 3 2 lisi male 45 [email protected] 15608492523 4 4 3 wmzi female 61 [email protected] 18848796505 5 5 4 wangw female 31 [email protected] 18848796506 6 6 5 liub male 56 [email protected] 18848796507 7 7 6 zhangf female 34 [email protected] 18848796573 8 8 7 guany male 56 [email protected] 17048796508 9 9 8 zhaoy female 35 [email protected] 17058796503 10 10 9 huangz male 65 [email protected] 13648796593 11 11 10 lvbu male 43 [email protected] 18148796803 12 12 11 sunwk male 99 [email protected] 18148706803 13 1 ID name gender age email phone 14 2 1 zhangs female 34 [email protected] 13423394013 15 3 2 lisi male 45 [email protected] 15608492523 16 4 3 wmzi female 61 [email protected] 18848796505 17 5 4 wangw female 31 [email protected] 18848796506 18 6 5 liub male 56 [email protected] 18848796507 19 7 6 zhangf female 34 [email protected] 18848796573 20 8 7 guany male 56 [email protected] 17048796508 21 9 8 zhaoy female 35 [email protected] 17058796503 22 10 9 huangz male 65 [email protected] 13648796593 23 11 10 lvbu male 43 [email protected] 18148796803 24 12 11 sunwk male 99 [email protected] 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 [email protected] 13423394013 10 lvbu male 43 [email protected] 18148796803 11 sunwk male 99 [email protected] 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 [email protected] 13423394013 3 wmzi female 61 [email protected] 18848796505 4 wangw female 31 [email protected] 18848796506 5 liub male 56 [email protected] 18848796507 6 zhangf female 34 [email protected] 18848796573 7 guany male 56 [email protected] 17048796508 8 zhaoy female 35 [email protected] 17058796503 9 huangz male 65 [email protected] 13648796593 10 lvbu male 43 [email protected] 18148796803 11 sunwk male 99 [email protected] 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 [email protected] 13423394013 ID name gender age email phone 1 zhangs female 34 [email protected] 13423394013 2 lisi male 45 [email protected] 15608492523 3 wmzi female 61 [email protected] 18848796505 4 wangw female 31 [email protected] 18848796506 5 liub male 56 [email protected] 18848796507 6 zhangf female 34 [email protected] 18848796573 7 guany male 56 [email protected] 17048796508 8 zhaoy female 35 [email protected] 17058796503 9 huangz male 65 [email protected] 13648796593 10 lvbu male 43 [email protected] 18148796803 11 sunwk male 99 [email protected] 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 [email protected] 13423394013 2 lisi male 45 [email protected] 15608492523 10 lvbu male 43 [email protected] 18148796803 11 sunwk male 99 [email protected] 18148706803 [root@lgh test]# awk '/^1/{print;getline;print;exit}' test.txt #執行了getline,print,然後exit了 1 zhangs female 34 [email protected] 13423394013 2 lisi male 45 [email protected] 15608492523 [root@lgh test]# awk '/^1/{print;if((getline var)<0)exit;print var}' test.txt #getline賦值給了var 1 zhangs female 34 [email protected] 13423394013 2 lisi male 45 [email protected] 15608492523 10 lvbu male 43 [email protected] 18148796803 11 sunwk male 99 [email protected] 18148706803 [root@lgh test]# awk '/^1/{print;if((getline var)<0)exit;print $0}' test.txt #getline賦值給了var,但是沒有設定$0 1 zhangs female 34 [email protected] 13423394013 1 zhangs female 34 [email protected] 13423394013 10 lvbu male 43 [email protected] 18148796803 10 lvbu male 43 [email protected] 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 [email protected] 163.com [email protected] gmail.com [email protected] sohu.com [email protected] sohu.com [email protected] 163.com [email protected] 139.com [email protected] 189.com [email protected] 163.com [email protected] 189.com [email protected] sohu.com [email protected] 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-
https://www.cnblogs.com/f-ck-need-u/p/12688355.html
https://www.cnblogs.com/hdk1993/p/4637525.