"三劍客"之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的執行過程

  1. 自動從指定的數據文件中讀取一個數據行.
  2. 自動更新(Update)相關的內建變量之值. 如 : NF, NR, $0...
  3. 依次執行程序中 所有 的 Pattern { Actions } 指令.
  4. 當執行完程序中所有 Pattern { Actions } 時, 若數據文件中還有未讀取的數據, 則反覆執行步驟1到步驟4.

1.3、本文實例數據

ID      name    gender  age     email           phone
1       zhangs  female  34      aaa@163.com     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      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      [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      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      [email protected]   15608492523
3       wmzi    female  61      [email protected]    18848796505
4       wangw   female  31      [email protected]    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      [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      aaa@163.com     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      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      [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      aaa@163.com     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
1-zhangs-female-34-aaa@163.com-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-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[email protected]18148796803
11-sunwk-male-99[email protected]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      [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      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      [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      aaa@163.com     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      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      [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      aaa@163.com     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      aaa@163.com     13423394013
3       wmzi    female  61      [email protected]    18848796505
4       wangw   female  31      [email protected]    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      [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      aaa@163.com     13423394013
ID      name    gender  age     email           phone
1       zhangs  female  34      aaa@163.com     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      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      [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"。

其中:

  1. getline:會從主輸入文件中讀取記錄。會同時設置$0,NF,NR,FNR。
  2. getline var:會從主輸入文件中讀取記錄,並將讀取的記錄賦值給變量var。會同時設置var,NR,FNR。
  3. getline <file:從外部文件file中讀取記錄。同時會設置$0,NF。
  4. getline var <file:從外部文件file中讀取記錄,並將讀取的記錄賦值給變量var。會同時設置var。
  5. cmd | getline:從管道中讀取記錄。會同時設置$0,NF。
  6. 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      [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      aaa@163.com     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      aaa@163.com     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      aaa@163.com     13423394013
1       zhangs  female  34      aaa@163.com     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
aaa@163.com
163.com
[email protected]
gmail.com
[email protected]
sohu.com
[email protected]
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
[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.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章