這部分的內容許多在以往的筆記中有涉獵,因此大多數不會詳述,
內置(built-in)和預定義(predefined)雖然名字不同,不過含義是等價的,官方檔案中也同時使用到這兩個英文詞匯,
選項
-e:指定awk代碼,一般代碼可以直接寫在CLI或者使用-f來指定代碼檔案,不過這兩種只能二選一,如果已經使用-f指定了代碼檔案還想在CLI中寫代碼的話,就得寫在-e中,
-e program-text --source program-text awk -f code.awk -e '...cliCode...' FILENAME
-f:指定awk的代碼檔案,一般如果代碼內容較多不適合寫在CLI的情況下會寫在某個單獨的檔案中,
-f source-file --file source-file
-F:指定輸入欄位分隔符,
-F fs
--field-separator fs
-n:默認情況下,awk會將來自輸入資料的數值均識別為十進制,即使該數值以0或者0x開頭,而使用-n選項的話,可以根據數值的前綴自動識別為八進制或者十六進制,
-n --non-decimal-data # echo "030" | awk '{print $1+0}' 30 # echo "030" | awk -n '{print $1+0}' 24
-o:將CLI中的awk代碼格式化后輸出到外部檔案中,
-o[file] --pretty-print[=file] # awk -o 'NR==1{print}' a.txt # cat awkprof.out NR == 1 { print $0 }
-v:變數賦值,在BEGIN代碼塊中可用,
-v var=val
--assign var=val
內置變數
內置變數大體可以分為兩類:控制awk作業類和攜帶資訊類,
控制awk作業類
RS:(輸入)記錄分隔符,默認為換行符,詳見讀取檔案,
IGNORECASE:在正則匹配時是否忽略大小寫,要注意設定的位置,在設定位置之后的作業流才生效,需要對awk作業流程有一定的了解,
FS:按照欄位分隔符取欄位,詳見讀取檔案,
FIELDWIDTHS:按照欄位寬度取欄位,詳見讀取檔案,
FPAT:按照欄位的模式取欄位,詳見讀取檔案,
OFS:輸出欄位分隔符,print命令會使用到,
ORS:輸出記錄分隔符,print命令會使用到,
CONVFMT:數值隱式轉換成字串時所遵循的格式,默認值為“%.6g”,
OFMT:使用print命令輸出小數時,數值轉換成字串時所遵循的格式,默認值為“%.6g”,
攜帶資訊類
ARGC:引數的個數,詳見ARGC和ARGV等,
ARGV:保存各個引數,詳見ARGC和ARGV等,
ARGIND:ARGV中各引數的索引,詳見ARGC和ARGV等,
FILENAME:當前正在處理的檔案名,詳見ARGC和ARGV等,
ENVIRON:參考shell環境變數,詳見ARGC和ARGV等,
NR:當前已讀取的記錄數(可簡單理解為行號),當處理多個檔案時,NR不會重置而是會一直往上疊加,如果NR修改了,那么下一條記錄會基于新的NR值,
FNR:在當前檔案中已讀取的記錄數(可簡單理解為行號),當處理多個檔案時,NR會重置,如果NR修改了,那么下一條記錄會基于新的NR值,
NF:當前記錄的欄位數,詳見讀取檔案,,
RT:每次記錄分隔時所采用的具體的記錄分隔符,詳見讀取檔案,
RLENGTH:詳見下文內置函式match(),
RSTART:詳見下文內置函式match(),
SUBSEP:多維陣列中索引分隔字符,詳見陣列,
內置函式
數值類
int(x):取整函式,向0位置方向取整,也可以理解為直接截斷小數部分,
# awk 'BEGIN{print int(3)}' 3 # awk 'BEGIN{print int(3.9)}' 3 # awk 'BEGIN{print int(-3)}' -3 # awk 'BEGIN{print int(-3.9)}' -3
對于包含字母字串的,能截斷取整就截斷取整,不行就回傳0,
# awk 'BEGIN{print int("3.14abc")}' 3 # awk 'BEGIN{print int("abc3.14")}' 0
sqrt(x):回傳正整數的平方根,遇到負數就報錯,
# awk 'BEGIN{print sqrt(9)}' 3 # awk 'BEGIN{print sqrt(4)}' 2 # awk 'BEGIN{print sqrt(-4)}' awk: cmd. line:1: warning: sqrt: called with negative argument -4 -nan
rand():回傳亂數,亂數位于[0,1),
# awk 'BEGIN{print rand()}' 0.924046
一般來說我們期望獲得一個隨機整數,那么就會使用一個整數與之相乘,然后再結合int(),例如取得一個位于[0,10)之間的亂數,則乘以10,
# awk 'BEGIN{print 10*rand()}' 9.24046 # awk 'BEGIN{print int(10*rand())}' 9
如果你反復運行rand(),就會發現其每次生成的亂數都是固定的,哪怕在SSH會話中重新連接或者新建會話視窗,
# awk 'BEGIN{print rand()}' 0.924046 # awk 'BEGIN{print rand()}' 0.924046 # awk 'BEGIN{print rand()}' 0.924046
因為在大多數awk實作中(包含gawk,不包含mawk),每次運行awk開始生成亂數都會基于一個相同的數值或者說是種子(seed),只要這個種子的值不變,那么亂數就不會變,如果我們期望每次使用rand()生成亂數時得到的數字是真隨機的話,就需要使用srand()來修改種子值,
srand([x]):不帶引數的srand()可以設定一個隨機的種子值,使得rand()回傳真亂數,
默認情況下,srand()將當前的日期和時間(精確到秒)作為種子,也就是說如果兩次awk執行位于同一秒中,那么使用的種子相同,生成的亂數自然也就相同,
# awk 'BEGIN{srand();print rand()}' 0.929717 # awk 'BEGIN{srand();print rand()}' 0.145049 # awk 'BEGIN{srand();print rand()}' 0.145049
srand()會回傳前一次的種子,每次都是1,也就是當我們不使用srand()指定種子的時候,每次都是使用1作為種子,所以結果也就相同了,
# awk 'BEGIN{print rand()}' 0.924046 # awk 'BEGIN{print rand()}' 0.924046 # awk 'BEGIN{print srand()}' 1 # awk 'BEGIN{print srand()}' 1 # awk 'BEGIN{srand(1);print rand()}' 0.924046 # awk 'BEGIN{srand(1);print rand()}' 0.924046
讓我們結合以上所學,生成一個位于[10,100]的亂數,
awk 'BEGIN{srand();print int(10+91*rand())}'
字串類
sprintf(format, expression1, ...):詳見輸出操作,
length([string]):這個我們見過很多了,回傳字符數量,如果引數是陣列則回傳陣列元素的數量,詳見陣列,如果引數為空的話則回傳$0的字符數量,
在回傳字符數量的時候有一些需要注意的點,比如我們回傳一個小數的時候,小數點也屬于字符數量的統計范圍中,有的時候回傳的字符數量不對,是因為可能遇到了需要使用OFMT或者CONVFMT來根據默認值“%.6g”來轉換的情況,
# awk 'BEGIN{print length(100)}' 3 # awk 'BEGIN{print length(100.123)}' 7 # awk 'BEGIN{print length(100.123456)}' 7 # awk 'BEGIN{print 100.123456}' 100.123 # awk 'BEGIN{print length(1000000000000000.123456)}' 5 # awk 'BEGIN{print 1000000000000000.123456}' 1e+15
strtonum(str):詳見語法,如果str以0、0x或者0X開頭則會識別成對應的八或者十六進制以后再轉換,
# awk 'BEGIN{print strtonum("010")}' 8 # awk 'BEGIN{print strtonum("0x10")}' 16 # awk 'BEGIN{print strtonum("0X10")}' 16
tolower(str)和toupper(str):大小寫的轉換,
# awk 'BEGIN{print tolower("aBcDeFg")}' abcdefg # awk 'BEGIN{print toupper("aBcDeFg")}' ABCDEFG
index(str,substr):在字串str中尋找子字串(簡稱子串)substr,若找到則回傳子串substr在字串str中的起始位置,若找不到則回傳0,
注意:在awk中涉及到字符索引位置的函式,其索引位置都是從1開始計算,其他大多編程語言則是從0開始計算,
# awk 'BEGIN{print index("alongdidi","di")}' 6 # awk 'BEGIN{print index("alongdidi","zzz")}' 0
基于這個特性我們可以使用該函式來判斷A字串是否包含B字串的功能,類似正則匹配功能,
# awk '$5~/qq.com/{print}' a.txt 1 Bob male 28 [email protected] 18023394012 8 Peter male 20 [email protected] 17729348758 # awk 'index($5,"qq.com"){print}' a.txt 1 Bob male 28 [email protected] 18023394012 8 Peter male 20 [email protected] 17729348758
substr()
substr(string,start[,length])
substr()函式的作用是從給定的字串string中,根據給定的索引起始位置和長度來提取子串,
# awk 'BEGIN{print substr("alongdidi",3,3)}' ong
長度可以省略,表示提取到字串結束為止,
# awk 'BEGIN{print substr("alongdidi",3)}' ongdidi
起始位置如果是非正整數,那么表示從1開始,如果起始位置大于字串長度,那么回傳空字串,
# awk 'BEGIN{print substr("alongdidi",-1)}' alongdidi # awk 'BEGIN{print substr("alongdidi",0)}' alongdidi # awk 'BEGIN{print substr("alongdidi",20)}'
如果子串的長度是非正整數,那么回傳空字串,
# awk 'BEGIN{print substr("alongdidi",1,0)}' # awk 'BEGIN{print substr("alongdidi",1,-1)}'
split()和patsplit()
split(string,array[,fieldsep[,seps]])
split()函式根據欄位分隔符fieldsep將字串string分割成各個欄位并存入陣列array中,由于fieldsep支持正則運算式,因此每次切分欄位的欄位分隔符可能不同,將每次實際的欄位分隔符存入陣列seps中,這個關系有點類似于RS和RT的關系,
# awk 'BEGIN{split("a b c d",arr);for(i in arr){print i"-->"arr[i]}}' 1-->a 2-->b 3-->c 4-->d
如果不指定欄位分隔符,那么默認使用FS,即默認值為空格,因此一個或多個空格均可作為默認的欄位分隔符,
關聯陣列array的索引是從1開始的,
函式的回傳值是切割得到的欄位個數(即陣列元素的長度),
# awk 'BEGIN{print split("a b c d",arr)}' 4
使用正則欄位分隔符分割欄位,實際欄位分隔符的陣列索引也是從1開始,
# awk 'BEGIN{split("a1b22c333d",arr,"[[:digit:]]+",seps);for(i in arr){print i"-->"arr[i]};for(i in seps){print i"-->"seps[i]}}' 1-->a 2-->b 3-->c 4-->d 1-->1 2-->22 3-->333
split()函式在將分割后的欄位寫入陣列之前,會清空該陣列,因此可以將待分割的字串設定為空,從而用來清空某個陣列,不過清空陣列直接”delete arr“即可,此可是了解split()的作業原理,
# awk 'BEGIN{for(i=1;i<=3;i++){arr[i]=i};print length(arr);split("",arr);print length(arr)}' 3 0
如果split()函式根據已有條件無法分割字串的話,則會將整個字串當作一個欄位存入陣列,
# awk 'BEGIN{split("alongdidi",arr);for(i in arr){print i"-->"arr[i]}}' 1-->alongdidi
patsplit()函式,
patsplit(string,array[,fieldpat[,seps]])
split()和patsplit()的區別在于前者使用欄位分隔符分隔欄位,后者使用欄位模式來匹配欄位,它們的關系類似于FS和FPAT的關系(因此這里就不再贅述patsplit()的用法,不懂的就去看看鏈接中的文章,),如果省略fieldpat,則按照FPAT的值來,
# awk 'BEGIN{patsplit("aaa1bbb22ccc333",arr,"[[:alpha:]]+",seps);for(i in arr){print i"-->"arr[i]};for(j in seps){print j"-->"seps[j]}}' 1-->aaa 2-->bbb 3-->ccc 0--> # 請留意這里的實際欄位分隔符以0開始, 1-->1 2-->22 3-->333
match()
match(string,reg[,arr])
使用正則運算式在字串中進行匹配,匹配成功則回傳匹配成功部分最開始的索引位置,匹配失敗則回傳0,
# awk 'BEGIN{print match("alongdidi","(di)+")}' # 從字串alongdidi的第6個位置開始匹配成功, 6 # awk 'BEGIN{print match("alongdidi","z+")}' 0
可以指定陣列引數arr,將整個匹配到的結果存入arr[0],如果正則中使用了分組捕獲,那么依照順序將每個分組捕獲到的資料存入arr[1], arr[2], ...,
# awk 'BEGIN{match("foo+++bar+++baz+++","(foo)\\++(bar)\\++(baz)\\++",arr);for(i=0;i<=3;i++){print i"-->"arr[i]}}'
0-->foo+++bar+++baz+++ 1-->foo 2-->bar 3-->baz
注意:CLI中想要正確將第一個+字符識別為字面量,必須使用雙反斜線,如果使用單反斜線會有警告,并且結果會例外,
awk: cmd. line:1: warning: escape sequence `\+' treated as plain `+'
如果我們使用遍歷陣列的方式會發現還存盤有其他陣列元素,顧名思義是每個元素的在原字串中的起始位置以及長度,
0start-->1 0length-->18 3start-->13 1start-->1 2start-->7 3length-->3 2length-->3 1length-->3
如果匹配成功,則會將arr[0]的起始位置和長度存入預定義變數RSTART和RLENGTH,等同于arr[0start]和arr[0length],
# awk 'BEGIN{match("alongdidi","(di)+");print RSTART,RLENGTH}' 6 4
如果匹配失敗,則RSTART等于0,RLENGTH等于-1,
# awk 'BEGIN{match("alongdidi","z+");print RSTART,RLENGTH}' 0 -1
這種匹配成功回傳正整數匹配失敗回傳0的特性,可以用來表示布林值的真偽,因此可以用作if條件判斷或者pattern,
# awk 'match($2,"A.+"){print}' a.txt 2 Alice female 24 [email protected] 18084925203 5 Alex male 18 [email protected] 18185904230 6 Andy female 22 ddd@139.com 18923902352
sub()、gsub()和gensub()
這三個函式都是字串替換(substitute)函式,它們的作業方式和sed或者vim中的基于正則匹配替換相似,
sub(regexp,replacement[,target])
gsub(regexp,replacement[,target])
在target中匹配正則regexp,如果匹配到則將其替換成replacement,并把替換后的結果重新賦值給target,因此target必須是可賦值的(變數名、陣列元素名或者$N等),不能是字面量,
sub()和gsub()唯一的區別在于后者是全域替換(global),sub()回傳1或者0來表示替換成功或者失敗,gsub()回傳正整數或者0來表示替換成功的次數或者替換失敗,
如果省略target的話,則使用$0,如果target是$0或者$N,由于函式會將target重新賦值,因此就涉及到$0或者$N的重建/重新計算,詳見讀取檔案中的【欄位與記錄的重建】部分,
# awk 'BEGIN{str="aooboocoo";count=sub("oo","xx",str);print count;print str}' 1 axxboocoo # awk 'BEGIN{str="aooboocoo";count=gsub("oo","xx",str);print count;print str}' 3 axxbxxcxx
在替換時(replacement中)可以使用&來參考匹配成功的部分,
# awk 'BEGIN{str="aooboocoo";count=sub("oo","x&x",str);print str}' axooxboocoo # awk 'BEGIN{str="aooboocoo";count=gsub("oo","x&x",str);print str}' axooxbxooxcxoox
但是,sub()和gsub()均不支持“\N”(N為正整數)的形式來反向參考,
gensub()相對于前兩者的最大區別在于其支持反向參考,并且gensub()可以完全代替前兩者,
gensub(regexp, replacement, how[, target])
how:用來指定對第幾個匹配到的字串進行替換,1等同于sub(),使用g/G開頭的字串等同于gsub(),也可以指定其他正整數,
gensub()回傳替換成功后的結果而不是替換成功的次數;如果匹配失敗則回傳target本身,
# awk 'BEGIN{str="aooboocoo";count=gensub("oo","x",1,str);print count;print str}' axboocoo aooboocoo # awk 'BEGIN{str="aooboocoo";count=gensub("oo","x","g",str);print count;print str}' axbxcx aooboocoo # awk 'BEGIN{str="aooboocoo";count=gensub("zz","x","g",str);print count;print str}' aooboocoo aooboocoo
反向參考需要使用兩根反斜線,awk對于轉義(反斜線)的解釋比較混亂,有時候需要1根,有需要需要2根,這個需要自行測驗,\0等同于&,
# awk 'BEGIN{str="abc def";new=gensub("(.+) (.+)","\\2|\\1|\\0|&","global",str);print new}' def|abc|abc def|abc def
asort()和asorti()
asort(src[, dest[, how]])
asorti(src[, dest[, how]])
這兩個函式用來對陣列進行排序,
asort()對陣列的元素值進行排序,排序的原則基于how(也就是說可以按照預定排序規則或者自定義函式),排序完成以后將陣列的索引值修改為正整數序列(1, 2, 3, ...),
asorti()與asort()的區別在于其是對陣列的索引值(index/indices)進行排序,
這部分,作者在視頻中沒有講解,可能是較少使用或者較復雜,暫時留白,
IO類
1、close(),在介紹getline時已講解,
close(filename[,how])
2、fflush(),
fflush([filename])
flush任何與filename相關的輸出,filename可以是一個已打開用于寫入的檔案或者是一個用于重定向輸出到管道或者協程的shell命令,
許多程式都會緩沖自己的輸出,相對于一有一點點資料就立刻將其輸出來說,緩沖機制會是效率更高,不過有時候也有必要在緩沖區還沒有滿的時候就輸出資料,因此awk提供了fflush()函式來實作這個功能,
從4.0.2開始,如果fflush()函式沒有引數或者引數是空字串,awk會flush所有的緩沖,
fflush() # 無引數
fflush("") # 引數為空字串
當將資料輸出至管道或者協程時可能會被緩沖,而輸出到終端是行緩沖,遇到換行即輸出,此前我們說的緩沖一般叫塊緩沖,可以緩沖多行的資料,
# awk '{print $1+$2}'
1 1 # 鍵入1空格1回車
2 # 立即回傳2
2 3 # 鍵入2空格3回車
5 # 立即回傳 5
# 鍵入Ctrl+d表示終止輸入
# awk '{print $1+$2}' | cat
1 1 # 鍵入1空格1回車
2 3 # 沒有回傳輸出結果,再次鍵入2空格3回車
2 # 依然沒有回傳,輸入Ctrl+d終止輸入后才回傳結果,此前的資料都被緩沖起來了,
5
使用flush就可以使得原本輸出到管道需要緩沖的資料立刻被flush,
awk '{print $1+$2;fflush()}' | cat
3、system(),在介紹getline時已講解,
system(command)
時間類
由于awk常用于處理日志檔案,而時間對于日志檔案來說是一個非常重要的概念,因此與時間相關的內置函式就顯得很重要了,
systime()
回傳當前系統時間距離epoch的秒數,我們可以勉強把它稱之為【epoch值】,在計算機領域中,和時間相關的epoch指的是“1970-01-01 00:00:00”,
# awk 'BEGIN{print systime()}' 1612173172
mktime()
mktime("YYYY MM DD HH MM SS [DST]"[,utc-flag])
根據用于給出的日期時間格式資訊,回傳對應的epoch值,
# awk 'BEGIN{print mktime("2021 02 01 18 05 00")}' 1612173900
mktime()比較智能,假如用戶輸入的時間超出范圍,會自動換算成對應的時間,甚至支持負數,
# awk 'BEGIN{print mktime("2021 02 01 18 05 65");print mktime("2021 02 01 18 06 05")}' 1612173965 1612173965 # awk 'BEGIN{print mktime("2021 02 01 18 05 -5");print mktime("2021 02 01 18 04 55")}' 1612173895 1612173895
如果日期時間格式不對或者回傳的時間戳超出范圍的話,mktime()回傳-1,
strftime()
strftime([format[,timestamp[,utc-flag]]])
將時間戳(timestamp)轉換成用戶給定的格式(format)輸出,
這里的格式和date命令中的格式類似,所有的格式詳見官方檔案,時間戳則是epoch值,因此該函式一般可以結合mktime()函式一起使用,
# awk 'BEGIN{print strftime("%F %T",mktime("2021 02 01 00 00 00"))}' 2021-02-01 00:00:00
如果省略時間戳,那么使用當前的日期和時間對應的時間戳,
# awk 'BEGIN{print strftime("%F %T")}' 2021-02-01 18:25:43
如果省略格式,則使用PROCINFO["strftime"]對應的值,
# awk 'BEGIN{print PROCINFO["strftime"];print strftime()}' %a %b %e %H:%M:%S %Z %Y Mon Feb 1 18:26:56 CST 2021
資料型別類
isarray()
isarray(x)
判斷變數x是否是陣列,如果是則回傳1,否則回傳0,
typeof()
typeof(x)
回傳變數x的資料型別,有以下值:
- array:陣列,
- regexp:正則字面量,
- number:數值,
- string:字串,
- strnum:形似數值的字串,詳見語法,
- unassigned:未賦值狀態,有參考過但是沒有賦值過,詳見語法,
- untyped:未宣告的狀態,也可以理解為未鍵入的(type本身也有從鍵盤鍵入的意思),就是既沒參考更沒賦值的,詳見語法,
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/256152.html
標籤:Linux
上一篇:飛機大戰運行:pygame.error: pygame is not initialized 的解決方法
下一篇:linux 查看檔案內容的命令
