十,awk内置函数

  在awk中,可以自定义函数,也有内置的函数,今天我们就来总结一些常用的内置函数。

   awk的内置函数大致可以分类为算数函数、字符串函数、时间函数、其他函数等

  

  算数函数

  最常用的算数函数有rand函数、srand函数、int函数。

  可以使用rand函数生成随机数,但是使用rand函数时,需要配合srand函数,否则rand函数返回的值将一直不变,示例如下。

[root@node1 ~]# awk 'BEGIN{print rand()}'
0.237788
[root@node1 ~]# awk 'BEGIN{print rand()}'
0.237788
[root@node1 ~]# awk 'BEGIN{print rand()}'
0.237788
[root@node1 ~]#

   可以看到,如果单纯的使用rand函数,生成的值是不变的,可以配合srand函数,生成一个大于0小于1的随机数,示例如下

[root@node1 ~]# awk 'BEGIN{srand();print rand()}'
0.100419
[root@node1 ~]# awk 'BEGIN{srand();print rand()}'
0.691322
[root@node1 ~]# awk 'BEGIN{srand();print rand()}'
0.355234
[root@node1 ~]# awk 'BEGIN{srand();print rand()}'
0.355234
[root@node1 ~]# 

   可以看到,上图中生成的随机数都是小于1的小数,如果我们想要生成整数随机数,可以将上述生成的随机数乘以100,然后截取整数部分,使用int函数可以截取整数部分的值,示例如下

[root@node1 ~]# awk 'BEGIN{srand();print rand()}'
0.563365
[root@node1 ~]# awk 'BEGIN{srand();print 100*rand()}'
13.1234
[root@node1 ~]# awk 'BEGIN{srand();print int(100*rand())}'
71
[root@node1 ~]#

   经过上述处理以后,可以得到一个小于100的随机整数。

 

  字符串函数

  我们可以使用gsub函数或sub函数替换某些文本,先来 看看gsub函数怎样使用。

  如果我们想要将如下文本中的第一列中的小写字母"l"都替换成大写字母"L",则可以使用gsub函数,示例如下。

[root@node1 ~]# cat test11
Allen Phillips
Green Lee
William Ken Allen
[root@node1 ~]# awk '{gsub("l","L",$1);print $0}' test11
ALLen Phillips
Green Lee
WiLLiam Ken Allen
[root@node1 ~]# 

   如上图所示,我们使用gsub函数,将小写字母"l"替换成大写字母"L",但是替换的范围只限于"$1",所以,当我们再次输出文本时,发现只有文本中的第一列中的小写字母"l"被替换成了大写字母"L",其他列中的小写字母"l"并未被替换,当然,如果你想要替换文本中所有的小写字母"l",则可以将上图中的"$1"换成"$0",或者省略gsub函数中的第三个参数,省略gsub中的第三个参数时,默认为"$0",示例如下。

[root@node1 ~]# awk '{gsub("l","L",$0);print $0}' test11
ALLen PhiLLips
Green Lee
WiLLiam Ken ALLen
[root@node1 ~]# awk '{gsub("l","L");print $0}' test11
ALLen PhiLLips
Green Lee
WiLLiam Ken ALLen
[root@node1 ~]# 

   看完上述示例,我想你应该已经明白了gsub函数的作用,没错,gsub函数会在指定范围内查找指定的字符,并将其替换为指定的字符串。

  其实,我们还可以根据正则表达式,替换字符串,示例如下。

[root@node1 ~]# awk '{gsub("[a-z]","6",$1);print $0}' test11
A6666 Phillips
G6666 Lee
W666666 Ken Allen
[root@node1 ~]# 

   那么sub函数与gsub函数有什么不同呢?我们来对比一下。

[root@node1 ~]# awk '{gsub("l","L",$1);print $0}' test11
ALLen Phillips
Green Lee
WiLLiam Ken Allen
[root@node1 ~]# awk '{sub("l","L",$1);print $0}' test11
ALlen Phillips
Green Lee
WiLliam Ken Allen
[root@node1 ~]# 

   细心如你一定已经发现了,当使用gsub函数时,gsub会替换指定范围内的所有符合条件的字符。

  而使用sub函数则不同,当使用sub函数时,sub函数只会替换指定范围内第一次匹配到的符合条件的字符。

  我们可以把gsub函数的作用理解为指定范围内的全局替换。

  可以把sub函数的作用理解为指定范围内的单次替换,只替换第一次匹配到的字符。

  这就是sub函数与gsub函数的为唯一的不同之处。

 

  

  我们可以通过length函数,获取到指定字符串的长度,示例如下

[root@node1 ~]# cat test11
Allen Phillips
Green Lee
William Ken Allen
[root@node1 ~]# awk '{ for(i=1;i<=NF;i++){print $i,length($i)}}' test11
Allen 5
Phillips 8
Green 5
Lee 3
William 7
Ken 3
Allen 5
[root@node1 ~]# 

   如上图所示,我们输出了文本中每个单词的长度,其实,length函数可以省略传入的参数,即不指定任何字符换,当省略参数时,默认使用"$0"作为参数,示例如下。

[root@node1 ~]# awk '{print length()}' test11
14
9
17
[root@node1 ~]# awk '{print length($0)}' test11
14
9
17
[root@node1 ~]# 

   我们可以使用index函数,获取到指定字符位于整个字符串中的位置,示例如下

[root@node1 ~]# awk '{print index($0,"Lee")}' test11 
0
7
0
[root@node1 ~]# cat test11
Allen Phillips
Green Lee
William Ken Allen
[root@node1 ~]# 

   上图中,我们使用index函数,在每一行中咋找字符串"Lee",如果Lee存在于当前行,则返回字符串Lee位于当前行的位置,如果Lee不存在于当前行,则返回0,表示当前行并不存在Lee,如上图所示,第二行中包含Lee,而且Lee位于第二行的第7个字符的位置,所以返回数字7。

 

  在前文中,我们在总结数组时,提到过一个函数,借助这个函数可以动态的生成数组,而不用手动的设置数组中每个元素的值,没错,这个函数就是split函数。通过split函数,我们可以将指定的字符串按照指定的分割符切割,将切割后的每一段赋值到数组的元素中,从而动态的创建数组,示例如下。

[root@node1 ~]# awk -v ts="大娃:二娃:三娃" 'BEGIN{ split(ts,huluwa,":");for(i in huluwa) {print i,huluwa[i]}}'
1 大娃
2 二娃
3 三娃
[root@node1 ~]# 

   如上图所示,我们通过split函数,将字符串ts切割了,以":"作为分割符,将分割后的字符串保存到了名为huluwa的数组中,当我们输出数组中的元素时,每个元素的值为分割后的字符,其实,split函数也有对应的返回值,其返回值就是分割以后的数组长度,示例如下。

[root@node1 ~]# awk -v ts="大娃:二娃:三娃" 'BEGIN{print split(ts,huliwa,":")}'
3
[root@node1 ~]# 

  注意,被split函数分割后的数组的元素下标从1开始,不像其他语言中的数组下标是从0开始的,而且数组中元素输出的顺序可能与字符串中字符的顺序不同,原因在前文中已经说过了,如果我们想要按照顺序输出数组中的元素,可以使用如下方法。

[root@node1 ~]# awk -v ts="qq te ab th" 'BEGIN{ arrlen=split(ts,arr," ");for(i in arr){print i,arr[i]}}'
4 th
1 qq
2 te
3 ab
[root@node1 ~]# awk -v ts="qq te ab th" 'BEGIN{ arrlen=split(ts,arr," ");for(i=1;i<=arrlen;i++){print i,arr[i]}}'
1 qq
2 te
3 ab
4 th

   我们先使用了split函数生成了数组,并且将split的返回值保存在变量arrlen中,然后利用for循环中变量的递增,顺序的输出了数组中的对应下标以及元素值,如果你不明白为什么,请参考前文。

 

  其他函数

  我们还能够通过asort函数根据元素的值进行排序,但是,经过asort函数排序过后的数组的下标将会被重置,示例如下

[root@node1 ~]# awk 'BEGIN{t["a"]=66;t["b"]=88;t["c"]=3;for(i in t) {print i,t[i]}}'
a 66
b 88
c 3
[root@node1 ~]# awk 'BEGIN{t["a"]=66;t["b"]=88;t["c"]=3;asort(t);for(i in t) {print i,t[i]}}'
1 3
2 66
3 88
[root@node1 ~]# 

   如上图所示,数组中元素的值均为数字,但是下标为自定义的字符串,通过asort函数对数组排序后,再次输出数组中的元素时,已经按照元素的值的大小进行了排序,但是,数组的下标也被重置为了纯数字,其实,asort还有一种用法,就是在对原数组元素值排序的同时,创建一个新的数组,将排序后的元素放置在新数组中,这样能够保持原数组不做任何改变,我们只要打印新数组中的元素值,即可输出排序后的元素值,示例如下。

[root@node1 ~]# awk 'BEGIN{t["a"]=66;t["b"]=88;t["c"]=3;asort(t,newt);for(i in t) {print i,t[i]}}'
a 66
b 88
c 3
[root@node1 ~]# awk 'BEGIN{t["a"]=66;t["b"]=88;t["c"]=3;asort(t,newt);for(i in newt) {print i,newt[i]}}'
1 3
2 66
3 88
[root@node1 ~]# 

 

 

   其实,asort函数也有返回值,它的返回值就是数组的长度,换句话说,asort的返回值就是数组中元素的数量,示例如下。

[root@node1 ~]# awk 'BEGIN{t["a"]=66;t["b"]=88;t["c"]=3;len=asort(t,newt);for(i=1;i<=len;i++) {print i,newt[i]}}'
1 3
2 66
3 88
[root@node1 ~]# 

   理解完asort 函数,我们来认识一下asorti 函数,仔细看,是 asort 与 asorti

  使用asort 函数可以根据元素的值进行排序,而使用asorti 函数可以根据元素的下标进行排序。

  当元素的下标为字符串时,我们可以使用asorti 函数,根据下标的字母顺序进行排序,当元素的下标为数字时,我们就没有必要使用函数排序了,直接使用for循环即可排序,所以,此刻我们只考虑数组的下标为字符串时,怎样通过asorti 函数根据下标对数组进行排序。

  当数组的下标为字符串时,asorti 函数会根据原数组中的下标的字母顺序进行排序,并且将排序后的下标放置到一个新的数组中,并且asorti函数会返回新的数组的长度,示例如下

[root@node1 ~]# awk 'BEGIN{t["z"]=66;t["q"]=88;t["a"]=3;len=asorti(t,newt);for(i=1;i<=len;i++) {print i,t[i]}}'
1 
2 
3 
[root@node1 ~]# awk 'BEGIN{t["z"]=66;t["q"]=88;t["a"]=3;len=asorti(t,newt);for(i=1;i<=len;i++) {print i,newt[i]}}'
1 a
2 q
3 z

   如上图所示,asorti 函数根据数组t的下标排序后,创建了一个新的数组newt,newt中元素的值即为t数组下标的值,上例中,我们使用len变量保存了asorti函数的返回值,并且输出了最后排序后的新数组。

  那么,聪明如你,一定想到了,既然我们已经将t数组的下标排序输出了,那么我们一定可以根据排序后的下标再次输出对应的元素值,从而达到根据数组下标排序后,输出原数组元素的目的,示例如下。

[root@node1 ~]# awk 'BEGIN{t["z"]=66;t["q"]=88;t["a"]=3;len=asorti(t,newt);for(i=1;i<=len;i++) {print i,newt[i]}}'
1 a
2 q
3 z
[root@node1 ~]# awk 'BEGIN{t["z"]=66;t["q"]=88;t["a"]=3;len=asorti(t,newt);for(i=1;i<=len;i++) {print i,newt[i],t[newt[i]]}}'
1 a 3
2 q 88
3 z 66
[root@node1 ~]# 

   没错,上述过程,其实就是新数组负责排序老数组的下标,并将排序后的下标作为新数组的元素,而我们输出新数组元素的同时,又将新数组的元素值作为老数组下标,从而输出了老数组中的元素值,这句话好绕.



  

posted @ 2020-09-18 11:10  minseo  阅读(453)  评论(0编辑  收藏  举报