1.什么是變量
變量即變化的量,核心是“變”與“量”二字,變即變化,量即衡量狀態。
name='egrep'
#name是變量名 egrep是變量的值.
這篇文章我們就來詳細的總結一下awk中的變量,我們會先對概念進行描述,如果概念中有不明白的地方,不要着急,對應其示例,你自然就會明白。
2.awk變量分類
awk來說"變量"又分為"內置變量" 和 "自定義變量" , "輸入分隔符FS"和"輸出分隔符OFS"都屬於內置變量。
內置變量就是awk預定義好的、內置在awk內部的變量,而自定義變量就是用戶定義的變量。
我們先看看awk常用的一些內置變量,此處先大致列出其概念,只看概念並不容易理解其意思,不懂沒關系,等到示例時你自然會明白。
awk常用的內置變量以及其作用如下
FS:輸入字段分隔符, 默認為空白字符
OFS:輸出字段分隔符, 默認為空白字符
RS:輸入記錄分隔符(默認輸入是換行符), 指定輸入時的換行符
ORS:輸出記錄分隔符(輸出換行符),輸出時用指定符號代替換行符
NF:number of Field,當前行的字段的個數(即當前行被分割成了幾列),字段數量
NR:行號,當前處理的文本行的行號。
FNR:各文件分別計數的行號
FILENAME:當前文件名
ARGC:命令行參數的個數
ARGV:數組,保存的是命令行所給定的各參數
上面描述到的"輸入字段分隔符FS和輸出字段分隔符OFS在之前的文章中已經解釋過了,當前行字段數量NF也大致說了。
RS、ORS、NR、FNR、FILENAME、ARGC、ARGV這些術語對於我們來說是新接觸的,但是觸類旁通,RS其實與FS類似,ORS與OFS類似,FS是字段輸入分隔符,RS是行輸入分隔符,OFS是字段輸出分隔符,ORS是行輸出分隔符,它們的原理都很相似。不要着急,我們來慢慢解釋
3.內置變量實踐
內置變量NR (行號,當前處理的文本行號 )
首先,如下,test文件中一共有兩行的文本內容,使用空格隔開,第1行有4列,第2行有5列
[root@w ~]# awk '{print}' test
aaa 123 bbb 456
ccc 789 ddd 910 eee
而內置變量NR表示每一行的行號,內置變量NF表示每一行中一共有幾列,那么,也就是說,我們可以通過下例中的方法,得到test文本中,每一行的行號以及每一行對應的列的數量。
[root@w ~]# awk '{print NR,NF}' test 1 4 2 5
#這里的數字1和2 代表的是行號
#數字4和5 每一行字段的個數
或者,利用NR內置變量,先打印出行號,再打印出整行的內容,相當於為test1中的每一行都添加了行號以后再進行輸出,示例如下。
[root@w ~]# awk '{print NR,$0}' test 1 aaa 123 bbb 456 2 ccc 789 ddd 910 eee
[root@w ~]# cat -n test
1 aaa 123 bbb 456
2 ccc 789 ddd 910 eee
現在每一行的開頭都有行號了,簡單吧。
細心如你一定注意到了一個細節,就是在打印 $0 , $1 , $2 這些內置變量的時候,都有使用到"$"符號,但是在調用 NR , NF 這些內置變量的時候,就沒有使用"$",如果你有點不習慣,那么可能是因為你已經習慣了使用bash的語法去使用變量,在bash中,我們在引用變量時,都會使用$符進行引用,但是在awk中,只有在引用$0、$1等內置變量的值的時候才會用到"$",引用其他變量時,不管是內置變量,還是自定義變量,都不使用"$",而是直接使用變量名。
內置變量FNR
FNR內置變量是什么意思?我們一起來看看
當我們使用awk同事處理多個文件,並且使用NR顯示行號的時候,效果如下。
[root@w ~]# awk '{print}' test aaa 123 bbb 456 ccc 789 ddd 910 eee [root@w ~]# awk '{print}' test1 aaa#123bbb#456 ddd#789eee#910 [root@w ~]# awk '{print NR,$0}' test test1 1 aaa 123 bbb 456 2 ccc 789 ddd 910 eee 3 aaa#123bbb#456 4 ddd#789eee#910
從返回結果可以看出,awk處理多個文件的時候,如果使用NR顯示行號,那么,多個文件的所有行會按照順序進行排序。
可是,如果我們想要分別顯示兩個文件的行號,該怎么樣辦,這個時候我們就會用到內置的變量FNR,效果如下 >>
[root@w ~]# awk '{print}' test
aaa 123 bbb 456 ccc 789 ddd 910 eee [root@w ~]# awk '{print}' test1 aaa#123bbb#456 ddd#789eee#910
[root@w ~]# awk '{print FNR,$0}' test test1 1 aaa 123 bbb 456 2 ccc 789 ddd 910 eee 1 aaa#123bbb#456 2 ddd#789eee#910
我想,對比完上述兩個示例,你肯定明白了FNR內置變量的作用,沒錯,它的作用就是當awk處理多個文件時,分別對每個文件的行數進行計數。
內置變量RS
現在,我們來看看RS這個變量,我們說了,RS是輸入行分隔符,如果不指定,默認的"行分隔符"就是我們所理解的"回車換行"。
假設,我們不想以默認的"回車換行"作為"行分隔符",而是想使用空格作為所謂的行分隔符,也就是說,我們想讓awk認為,每遇到一個空格,就換行,換句話說,我們想讓awk以為每次遇到一個空格就是新的一行。那么我們該怎么做呢?示例如下。
[root@w ~]# cat test aaa 123 bbb 456 ccc 789 ddd 910 eee [root@w ~]# awk '{print NR,$0}' test 1 aaa 123 bbb 456 2 ccc 789 ddd 910 eee [root@w ~]# awk -v RS=" " '{print NR,$0}' test 1 aaa 2 123 3 bbb 4 456 ccc 5 789 6 ddd 7 910 8 eee [root@w ~]# awk 'BEGIN{RS=" "}{print NR,$0}' test 1 aaa 2 123 3 bbb 4 456 ccc 5 789 6 ddd 7 910 8 eee
如上所示,我們先使用了默認的"回車換行"作為"行分隔符"輸出了test1文本,這時顯示文本一共有2行。
而后來,我們又指定了使用"空格"作為"行分隔符"輸出test1文本,這時顯示文本一共有8行。
看到了嗎?當我們指定使用空格作為"行分隔符"時,在awk解析文本時,每當遇到空格,awk就認為遇到的空格是換行符,於是awk就將文本換行了,而此時人類理解的"回車換行",對於awk來說並不是所謂的換行符,所以才會出現上圖中第4行的現象,即使從人類的角度去看是兩行文本,但是在awk的世界觀里,它就是一行。
如果你還是沒有理解,那么我們換個方式描述,再來啰嗦一遍。
默認情況下,awk使用"回車換行"作為"行分隔符(換行符)",此時,人類的世界觀與awk的世界觀是一致的,因為我們和awk都認為,遇到回車換行,就表示當前行結束,開始新的一行。
而當我們指定了特定的"行分隔符"時,比如空格,那么當awk遇到空格時,就認為當前行結束了,新的一行開始了,此時,awk的世界觀與人類的世界觀已經不同,人類仍然認為"回車換行"才是新的一行,awk卻認為"回車換行"並不是新的一行的開始,所以,從上圖中返回的信息中,我們可以看到,人類所以為的"兩行",共用了一個行號,awk認為它們就是第4行。
這就是輸入行分隔符的使用方法。同理,我們來看看"輸出行分隔符",理解輸出行分隔符之前,請做好心理准備,最好不要以正常的思維去理解換行,才能比較容易的學明白輸出行分隔符。
內置變量ORS
在理解"輸出行分隔符"ORS之前,請先理解剛才描述的"輸入行分隔符"RS,否則理解起來可能比較困難。
默認情況下,awk將人類眼中的"回車換行",當做"輸出行分隔符",此時,awk的"世界觀"與人類的"世界觀"是相同的。
現在,我們改變一下awk的想法,我們讓awk認為,"+++"才是真正的輸出行分隔符,示例如下圖
[root@w ~]# cat test aaa 123 bbb 456 ccc 789 ddd 910 eee [root@w ~]# awk -v ORS='+++' '{print NR,$0}' test 1 aaa 123 bbb 456+++2 ccc 789 ddd 910 eee+++[root@w ~]#
看懂了嗎,我們再啰嗦的解釋一遍,在沒有指定輸出行分隔符之前,awk跟人類的邏輯思維是一樣一樣的,當人類想要換行的時候,就會"另起一行"(回車換行),awk也是一樣的,當它在輸出文字的時候,如果想要換行,就會"另起一行"(回車換行), 可是,如果我們指定了"輸出行分隔符"為"+++",那么,當awk在輸出文字的時候,如果想要換行,就會"另起一行"(+++),所以,對於awk來說,它完成了"另起一行"的動作,只不過,它所認為的"另起一行"的動作就是輸出"+++",而不再是原來的輸出" 回車換行",所以,從人類看到的"表象上",awk並沒有換行,那是因為我們還是以"回車換行"作為換行的標准,而awk已經變了,它認為,"+++"就是換行的標准。
這次明白了吧,真的明白了嗎?
我們把剛才學到的"輸入換行符"和"輸出換行符"同時使用,看看是什么效果,示例如下。

如果你能明白awk為什么會將test1的文本輸出成上圖中的模樣,那么你已經徹底理解了RS與ORS兩個內置變量。
如果你又懵逼了,那么,從RS內置變量開始,再看一遍吧。
[root@w ~]# cat test aaa 123 bbb 456 ccc 789 ddd 910 eee [root@w ~]# awk -v RS=" " -v ORS="+++" '{print NR,$0}' test 1 aaa+++2 123+++3 bbb+++4 456 ccc+++5 789+++6 ddd+++7 910+++8 eee
內置變量FILENAME
FILENAME這個內置變量,從字面上,就能看出是什么意思,沒錯,就是顯示文件名,演示效果如下。
[root@w ~]# cat test aaa 123 bbb 456 ccc 789 ddd 910 eee [root@w ~]# cat test1 aaa#123bbb#456 ddd#789eee#910 [root@w ~]# awk '{print FILENAME,FNR,$0}' test test 1 aaa 123 bbb 456 test 2 ccc 789 ddd 910 eee [root@w ~]# awk '{print FILENAME,FNR,$0}' test test1 test 1 aaa 123 bbb 456 test 2 ccc 789 ddd 910 eee test1 1 aaa#123bbb#456 test1 2 ddd#789eee#910
內置變量argc與 argv
ARGC內置變量表示命令行參數的個數,什么意思呢?我們先不解釋ARGC,先看看ARGV是什么。
別眼花了。
一個是ARGC,
一個是ARGV,
先說說ARGV。
ARGV內置變量表示的是一個數組,這個數組中保存的是命令行所給定的參數。這樣解釋還是很模糊,不容易理解,我們來看看示例。
[root@w ~]# awk 'BEGIN{print "aaa"}' test test1 aaa [root@w ~]# awk 'BEGIN{print "aaa" ARGV[1]}' test test1 aaatest [root@w ~]# awk 'BEGIN{print "aaa" ,ARGV[1]}' test test1 aaa test [root@w ~]# awk 'BEGIN{print "aaa",ARGV[1],ARGV[]2}' test test test1 [root@w ~]# awk 'BEGIN{print "aaa",ARGV[1],ARGV[2]}' test test1 aaa test test1
上圖中,我們先使用BEGIN模式,輸出一個字符串"aaa",然后,傳入兩個文件的文件名作為參數,我們發現,BEGIN模式正常執行了打印操作,輸出了"aaa"字符串 ,我們使用同樣的命令,同樣使用BEGIN模式,只不過,這次不只打印"aaa",還打印ARGV這個數組中的第二個元素的值。
我說已經說過,ARGV內置變量表示的是一個數組,既然是數組,就需要用上圖中的下標的方式,引用對應元素的值,因為數組的索引都是從0開始的,所以,ARGV[1]表示引用ARGV數組中的第二個元素的值,從返回結果可以看出,ARGV[1]對應的值為test1,同理,我們又使用第三條命令,多打印了一個ARGV[2]的值,發現ARGV[2]對應的值為test2,這個時候,你明白ARGV內置變量的含義了嗎,說白了,ARGV內置變量表示的是:所有參數組成的數組。那么細心的你一定會問了,ARGV[0]對應的是哪個參數呢,我們來打印一下。
[root@w ~]# awk 'BEGIN{print "aaa",ARGV[0],ARGV[1],ARGV[2]}' test test1 aaa awk test test1

我擦,第一個參數竟然是awk這個命令本身??太神奇了,有沒有很出乎意料···
好吧,awk就是這么規定的,'pattern{ action }'並不被看做是參數,awk被看做為參數。
好了,說明了ARGV變量以后,再說ARGC變量的作用,就容易多了。
在剛才的例子中,應該有三個參數,awk、test1、test2,這三個參數作為數組的元素存放於ARGV中,現在,而ARGC則表示參數的數量,也可以理解為ARGV數組的長度。示例如下
[root@w ~]# awk 'BEGIN{print "aaa"}' aaa [root@w ~]# awk 'BEGIN{print "aaa",ARGV[0]}' aaa awk [root@w ~]# awk 'BEGIN{print "aaa",ARGV[0],ARGV[1]}' aaa awk [root@w ~]# awk 'BEGIN{print "aaa",ARGV[0],ARGV[1]}' test aaa awk test [root@w ~]# awk 'BEGIN{print "aaa",ARGV[0],ARGV[1],ARGV[2]}' test test1 aaa awk test test1 [root@w ~]# awk 'BEGIN{print "aaa",ARGV[0],ARGV[1],ARGV[2],ARGC}' test test1 aaa awk test test1 3
#這里的3就是我們傳入參數總數,默認第一個參數元素是awk本身
自定義變量
好了,內置變量解釋完了,現在我們來看看自定義變量,自定義變量,顧名思義,就是用戶定義的變量,有兩種方法可以自定義變量。
方法一:-v varname=value 變量名區分字符大小寫。
[root@w ~]# awk -v name="egrep" "BEGIN{print name}" egrep
這種方式與設置內置變量的方法是一樣的
方法二:在program中直接定義。
直接在program中定義即可,但是注意,變量定義與動作之間需要用分號";"隔開。

當然,我們也可以一次性定義多個變量
[root@w ~]# awk 'BEGIN{name="egrep";print name}' egrep
[root@w ~]# awk 'BEGIN{name="egrep";age=18;print name,age}'
egrep 18
第一種方法雖然看上去比較麻煩,但是這種方法也有自己的優勢
當我們需要在awk中引用shell中的變量的時候,則可以通過方法一間接的引用。舉例如下

[root@w ~]# a=1
[root@w ~]# awk -v v1=$a 'BEGIN{print v1}'
1
好了,awk中變量的使用方法就暫時總結到這里,希望這篇文章會對你有所幫助。
