awk编程基础

https://www.cnblogs.com/meitian/p/5302416.html

https://www.cnblogs.com/Berryxiong/p/6240496.html

一、awk介绍

awk(名字来源于三个创始人姓氏首字母)是linux系统下文本编辑工具,是一门编程语言,有自己的基本语法和流程控制、函数。awk简单高效。

二、awk的运行方法

例子:使用冒号:分割/etc/passwd,打印出第一列

1.通过命令行执行

awk -F: '{print $1}' /etc/passwd

2.通过执行awk文件来执行

awk文件的格式:

#!/usr/bin/awk

BEGIN{ FS=":"}

{print $1}

然后调用这个awk文件的格式:awk -f awk脚本文件 /etc/passwd

三、awk的基本语法

awk通过FS作为每一段文本的分割符(默认空格),在命令行上可以用-F参数指定分隔符;

通过RS参数指定文本换行符(默认回车,所以是一行行取数据的),通过换行符作为分割来读取文件。这里区别于sed,sed是一行一行读取文件的

基本流程:

     BEGIN{在读取文件之前做的操作}

     {读取文件时做的操作}

     END{全部文件读取之后才进行的操作}

常用内置变量:

     $0  当前所有字段

     $1--$n 按照分隔符分割取到的第n列内容

     FS  分隔符(默认空格) awk 'BEGIN{FS=":"}{print $1}' /etc/passwd ;等价于awk -F: '{print $1}' /etc/passwd

     RS  换行符(默认回车) awk 'BEGIN{RS=":"}{print $1}' /etc/passwd

     NF  字符列数,当前处理行的分割后的列数 awk -F: '{print NF}' /etc/passwd

     NR  行号 awk -F: '{print NR ":" $1}' /etc/passwd

     OFS (默认空格)输出字段分隔符

     ORS (默认回车)输出记录分隔符

自定义外部变量:

     -v:自定义变量

     awk -v host=$HOSTNAME "BEGIN{print host}"

关系操作符:<、>、<=、>=、==、!=、~、!~

     比较符<等与其他的语言类似,重点说一下不一样的

     ~:用来判断前面的列是否匹配后面的内容。例如awk -F: '$7 ~ /^\/bin/{print $0}' /etc/passwd(判断第7列是否以/bin开头,如果是打印该列)

     !~:不匹配

输出:print与printf

     print:直接输出 awk -F: '{print $1 ":" $2}' /etc/passwd

     printf:格式化输出(printf是一个函数,需要用到())

          awk -F: '{printf(hello %s:%s\n),$1,$2}'

          注意:printf需要手动增加\n来换行。使用%s来格式化,printf()外加入要替换的变量

四、awk的流程控制

条件:

if语句 if(expression){action1}else{action2}

     例如:产生10个数seq 10,通过if语句判断是单数还是双数

     seq 10 |awk '{if($0%2==0){print $0"是双数"}else{print $0"是单数"}}'

如果只需要一个if分支,可以省略前面的if,比如awk -F: '$3<-10 {print $1}' /etc/passwd

循环:

while语句:while(expression){action}

     例子:使用:分割/etc/passwd,并将每一列前加上列号

     awk -F: '{i=1;while(i<=NF){print i":"$i;i++ }}' /etc/passwd

for语句:

第一种方法:for(i=0;i<=10;i++){action}

     例子:使用:分割/etc/passwd,并将每一列前加上列号

     awk -F: '{for(i=1;i<=NF;i++){print i":"$i}}' /etc/passwd

第二种方法:for(value in array){action}

当value在array的key中,进行下面的操作。awk的数组类似python中的字典。

     例子:统计/etc/passwd第7列的值及对应的个数

     awk -F: '{a[$7]++}END{for(i in a)if(i!=""){print i":"a[i]}}' /etc/passwd

     说明:a[$7]:将$7作为数组a的key,然后统计对应的个数;然后遍历for(i in a),判断i是否在数组a的key中;如果存在则打印a[i],a[i]为对应key的值,这里指个数。

数组:

array[1]="hello"

array["name"]="Jack"

数组类似python的字典,array[key值]="value值";key为索引,可以是数字也可以是字符串。

数组元素的删除:delete array["key"]

     例子:定义了数组a的三个值,并打印结果查看

     awk 'BEGIN{a[1]="hello";a[2]="word";a["name"]="meitian";for(i in a){print "key为"i":value为"a[i]}}'

  

五、awk函数

内置函数

1.算术函数:

int(x) 返回x的整数部分的值,值不会四舍五入,只是取整

sqrt(x) 返回x的平方根

rand() 返回伪随机数r,其中0<=r<1,(伪随机数指返回的值都是上一次返回的同一个随机数)

srand(x) 建立rand()新的种子数,如果没有指定就用当天的时间(使用srand()可以使得rand()返回不同的随机数)

     例子:rand()产生一个随机数,通过srand()产生新的种子数,然后再差生一个随机数

     awk 'BEGIN{print rand();srand();print rand()}'

2.字符串函数:

sub("要替换的字符串","替换后的字符串值"):替换匹配到的第一个文本

     echo "hello world world" | awk '{sub("world","meitian");print $0}'

gsub("要替换的字符串","替换后的字符串值" ):开启全局替换,替换文本中所有匹配到的字符串

     echo "hello world world" | awk '{gsub("world","meitian");print $0}'

     

index("a","b"):返回字符串b在字符串a中开始的位置

     awk 'BEGIN{print index("hello world","world")}'

length("s"):返回字符串s的长度,当没有指定s时,返回$0的长度

      awk -F 'BEGIN{print length("hello world")}{print lenght()}' /etc/passwd

match("s","r"):如果正则表达式r在s中匹配到,则返回出现的起始位置,否则返回0

     awk 'BEGIN{print match("hello world","[wo]")}'

split(s,a,sep) 使用sep将字符串s分解到数组a中,默认sep为FS。

     例子:使用o做为分隔符,将"hello world"进行分割存储到数组a中

     awk 'BEGIN{print split("hello world",a,"o");for(i in a){print a[i]}}'

  

toupper(s):将所有小写字母转换成大写字母

     echo "hello world" |awk '{print toupper($0)}'

tolower(s):将所有大写字母转换成小写字母

     echo "HELLO WORLD" |awk '{print tolower($0)}'

自定义函数:

function 函数名(参数1,参数2,...){语句;return 表达式}

     例子:求和

     awk 'function sum(a,b){total=a+b;return total}BEGIN{print sum(2,3)}'

注意:函数必须写在BEGIN{}{}END{}的花括号之外的地方,不能放在任何{}内,否则会报错`return' used outside function context

六、实战

1.获得eth0的IP地址

ifconfig eth0 | awk -F":| +" '/inet addr:/{print $4}'

ifconfig eth0的结果:

说明:

1.多个字符作为分隔符(比如例子中的冒号和空格),可以使用|来区分;或者直接使用正则来作区分。比如例子中的-F":| +"可以写成-F"[ :]+"

2.如果要过滤多个相同分隔符,可以使用正则的+。表示1个或多个

3.awk中可以使用'/操作匹配到该内容的行/{匹配到前面的行后进行的操作}'来选择某些想要的行。比如例子中需要取匹配到“inet addr:”的行,打印第4列,//中的为正则表达式,如果有/等需要使用\进行转义

2.统计tcp网络连接数

netstat -an |awk  '/^tcp/{a[$NF]++}END{for(i in a){printf("%s:%d\n",i,a[i])}}'

说明:与上面的例子大同小异,/^tcp/表示只处理以tcp开头的行。$NF表示最后一列 

七、在实战中可能用到的注意点

1.awk需要对文件进行处理。不需要处理文件的可以把打印命令写在BEGIN里(例如:awk -v name=meitian 'BEGIN{print name}')

2.可以对ls等命令结果进行处理

     例子:对包含conf的文件按照.进行分割,并将分割结果用冒号进行连接

     ls |grep conf |awk -F. '{print $1 ":" $2}'

3.awk使用-v定义变量,但是awk中引用变量时直接使用变量名,不需要在变量前加$

4.将awk中的结果赋值给变量传递给shell中用

方法一:可以使用eval()函数来将打印的结果转换成变量。eval会将打印的值当做命令来进行处理,shell中定义变量的格式:变量名=变量值

     例子:有个文件名为search.contract-0.0.5-SNAPSHOT.jar,按照-分割。将-前面的存为name变量,中间的版本号存为version变量。

     eval $(ls |grep search.contract-0.0.5-SNAPSHOT.jar |awk -F"-" '{printf("name=%s;version=%s\n",$1,$2)}')

  

方法二:如果只想保存一个变量,可以通过变量名=$(操作print变量值)的方法来保存  

     例如提取ifconfig eth0本机中的IP地址保存到变量a中

     a=$(ifconfig eth0 |awk -F":| *" '/inet addr/{print $4}')

  

5.只对包含某些内容的行进行操作

     awk '/要匹配的内容/{进行的操作}' file,//内可以放入正则表达式,注意对一些特殊字符进行转义,比如“[]\“

6.awk不能直接修改源文件,可以通过重导向输出结果来修改原文件。>(全部覆盖文件内容)或>>(追加到文件)

     awk '/^root/{print $0 >"passwd"}' passwd

     注意:重导向输出的文件名要用双引号括起来,否则会报错 

7.awk使用双引号作为分隔符,可以使用单引号括起来,'"'

如何在linux Shell脚本里面把一个数组传递到awk内部进行处理

前段时间和几位同事讨论过一个问题:Shell脚本里面怎样把一个数组传递到awk内部进行处理?
当时没有找到方法。前两天在QQ群里讨论awk的时候,无意间又聊起这个话题。机缘巧合之下找到一个思路,特此分享。
测试环境:
 [root]# head -1 /etc/redhat-release
Red Hat Enterprise Linux Server release 6.5 (Santiago)
[root]# awk --version | head -1
GNU Awk 3.1.7
众所周知,Shell脚本里面把一个普通变量传递给awk是非常简单的,直接用 -v 参数赋值就行了。
 str1="Hello World"
awk -v str2="$str1" 'BEGIN{print str2}'
但是,要把一个数组传递给awk就不是那么简单的事情了。请看以下三个试验:
1. 简单的数组可以先赋值后split
 arr1=(A B C)
awk -v arr2="${arr1[*]}" 'BEGIN{split(arr2,arr3," "); print arr3[2]}'
2. 有些情况下很难找到合适的分隔符来进行split,因为某个数组元素可能会包含你想用来作为分隔符的那个字符,这时split之后无法得到希望的结果。所以这个方法不够严谨,尤其是当我们无法预测数组元素可能包含哪些字符的时候。
 arr1=(A "B C" D)
awk -v arr2="${arr1[*]}" 'BEGIN{split(arr2,arr3," "); print arr3[2]}'
3. 可以借助export命令和awk的ENVIRON默认数组来实现这个功能
 arr1=(A "B C" D)
for((i=0;i<${#arr1[*]};i++)); do
  export arr1_m$i="${arr1[$i]}"
done
awk 'BEGIN{for(i in ENVIRON)if(i~/arr1_m/)print i "=" ENVIRON[i]}'
我这里只是为了演示功能,所以没有把export变量名的定义和awk内部的字符串匹配写的特别考究,大家可以根据实际情况进行调整(比如添加更多的限制条件等)。
结论:Shell脚本里面把一个数组传递到awk内部进行处理,技术上可行,但不建议在生产环境上使用

猜你喜欢

转载自blog.csdn.net/michaelwubo/article/details/81095735