awk是一种编程语言,gawk是目前最新的版本,当前的Linux版本用的都是gawk,利用gawk可以实现数据查找、抽取文件中数据、创建管道流命令等功能,awk实际是/bin/gawk的链接。
4.3.1 awk编程模型
awk程序由一个主输入循环main input loop维持,主输入循环反复执行,直到终止条件被触发,主输入循环自动依次读取输入文件行,以供处理,而处理文件行的动作是由程序员添加的。
awk还定义了两个特殊的字段:BEGIN和END,BEGIN用于在主输入循环之前执行,即在未读取输入文件之前执行,END则相反,用于在主输入循环之后执行,即在读取输入文件行完毕后执行。
4.3.2 awk调用方法
跟sed类似,也有三种方式:
① 在shell命令行直接输入命令调用,格式为:
awk [-F 域分隔符] 'awk程序段' 输入文件
② 将awk程序段插入脚本文件,然后通过awk命令调用它,格式为:
awk -f awk脚本文件 输入文件
③ 将sed命令插入脚本文件后,将该文件变成可执行,然后直接执行,不过需要一#!/bin/awk -f 开头
./awk脚本文件 输入文件
4.4.1 awk模式匹配
# awk '/^$/{print "this is a blank line."}' input --> 打印空白行,遇到空白行就打印一句话
第三种调用方式,先写个src.awk脚本文件:
#!/bin/awk -f /^$/{print "this is a blank line."}
然后chmod u+x src.awk,然后./src.awk input即可
4.4.2 记录和域
awk将每个输入文件行定义为记录,行中每个字符串定义为域,域之间用空格、Tab键或其他符号进行分割,称之为分隔符。
$1表示第一个域,$2表示第二个域以此类推。注:$0表示所有的域
使用 -F 选项指定分隔符:
# awk -F "\t" '{print $3}' test.txt
还可以通过设置FS的值改变分隔符:
# awk 'BEGIN {FS=","} {print $1}' test.txt
# awk 'BEGIN {FS="\t+"} {print $1}' test.txt --> 利用正则表达式设置分隔符
4.4.3 awk 关系和布尔运算符
运算符 | 意义 |
< | 小于 |
> | 大于 |
<= | 小于等于 |
>= | 大于等于 |
== | 等于 |
!= | 不等于 |
~ | 匹配正则表达式 |
!~ | 不匹配正则表达式 |
使用正则表达式~符号例子
# awk 'BEGIN {FS=":"} $1~/root/' /etc/passwd --> 在/etc/passwd文件中第一个域匹配root的
# awk 'BEGIN {FS=":"} $0~/root/' /etc/passwd --> 全部域匹配root的
# awk 'BEGIN {FS=":"} $0!~/nologin/' /etc/passwd --> 全部域不匹配nologin的
awk的条件语句示例
# awk 'BEGIN {FS=":"} {if($3<$4) print $0}' /etc/passwd
# awk 'BEGIN {FS=":"} {if($3==10 || $4==10) print $0}' /etc/passwd
# awk 'BEGIN {FS=":";count=0} {if($3>99) count++} END {print count}' /etc/passwd --> 列出系统中UID大于99的账号数量
4.4.4 表达式
+ - * / %(模) ^或**(乘方) ++x x++
# awk '/^$/ {print x+=1}' input
一个计算平均值的awk脚本:
#!/bin/awk -f BEGIN {FS=","} { total=$3+$4+$5 avg=total/3 print "result:" + avg }
4.4.5 awk系统变量:
变量名 | 意义 |
$n | 当前记录第 n个域,域间用FS分隔 |
$0 | 记录的所有域 |
ARGC | 命令行参数的数量 |
ARGIND | 命令行中当前文件的位置(以0开始标号) |
ARGV | 命令行参数数组 |
CONVFMT | 数字转换格式 |
ENVIRON | 环境变量关联数组 |
ERRNO | 最后一个系统错误描述 |
FIELDWIDTHS | 字段宽度列表,空格键分割 |
FILENAME | 当前文件名 |
FNR | 浏览文件的记录数 |
FS | 字段分隔符,默认是空格 |
IGNORECASE | 布尔变量,如果为真,忽略大小写 |
NF | 当前记录中的域数量 |
NR | 当前记录数,从1开始,表示当前是第几条记录 |
OFMT | 数字的输出格式 |
OFS | 输出域分隔符,默认是空格键 |
ORS | 输出记录分隔符,默认是\n |
RLENGTH | 由match函数所匹配的字符串长度 |
RS | 记录分隔符,默认是\n |
RSTART | 由match函数匹配的字符串的第1个位置 |
SUBSEP | 数组下标分隔符,默认是\034 |
另外还有个RT:
RT就是当RS为正则表达式时的匹配到的每个记录的分割符的内容即为RT变量表示,用到RS的时候一般可以用RT作为每次匹配的分割符的变量的值,靠,这么解释你还不清楚就去面壁去吧。^_^
# echo "111 222a333 444b555 666"|awk 'BEGIN{RS="[a-z]+"}{print $1,RS,RT}'
当RT是利用RS匹配出来的内容。如果RS是某个固定的值时,RT就是RS的内容
示例:打印19000行至20000行
# awk '{if(NR>= 19900 && NR<=20000) print $0}' /data/log/resin/resin.log
判断log日志中通过日期的比较来打印:
# awk -F, 'BEGIN{now=systime();aa=strftime("%Y-%m-%d %H:%M:%S", now)}{if ($2 > aa) print $0}' input
# awk -F, 'BEGIN{now=mktime("2013 10 05 12 12 12 333");aa=strftime("%Y-%m-%d %H:%M:%S", now)}{if ($2 > aa) print $0}' input
4.4.6 格式化输出
awk使用类似于C语言的printf函数格式化输出
基本语法是:printf (格式控制符, 参数)
# awk 'BEGIN {FS=","} {printf("%-15s\t%s\n",$1,$3)} ' input.txt
下面这个例子没有输入文件,直接打印一个数字,很神奇吧。
# awk 'BEGIN {printf("%-10.3f\n", 2009.12345)}'
4.4.7 内置字符串函数
字符串替换、查找、分割功能,有很多内置函数,let me see see:
函数名 | 意义 |
gsub(r,s) | 在整个$0中用s替换r |
gsub(r,s,t) | 在t中用s替换r,t可以是字符串也可以是域 |
index(s,t) | 返回s中第一个字符串t的位置,从1开始 |
length(s) | 返回s的长度 |
match(s,t) | 测试s是否包含匹配t的字符串,t可以是正则式 |
split(r,s,t) | 在t上利用分割正则式r分割成序列s |
sub(r,s,t) | 将t中第一次出现的r替换成s |
substr(r,s) | 返回字符串r中从s开始的后缀部分 |
substr(r,s,t) | 返回字符串r中从s开始长度为t的后缀部分 |
解释下
gsub函数执行字符串替换功能,它将第一个字符串替换成第二个字符串。gsub函数有两种形式,第一种形式作用于全部域$0,也就是gsub(r,s)。第二种形式作用于域t,也就是gsub(r,s,t)
// 第一条命令:替换第1域上的root字符串,注意我的输出的分隔符设定OFS
# awk 'BEGIN {FS=":"; OFS="+"} {gsub(/root/,"haha",$1); print $0}' /etc/passwd
# awk 'BEGIN {FS=":"; OFS="+"} gsub(/root/,"haha",$1) {print $0}' /etc/passwd
上面两行的席位区别,然后输出结果是:第一个命令会输出所有行,第二个只会输出gsub找到的那行,why
// index和length函数的用法
# awk 'BEGIN {print index("google.com", ".com")}'
# awk 'BEGIN {print length("google.com")}'
match(s,t)测试s是否包含正则表达式t字符串,若匹配成功,则返回t的首位置,若不成功,则返回0
# awk 'BEGIN {print match("grade one", /D/)}'
# awk 'BEGIN {IGNORECASE=1; print match("grade one", /D/)}'
// sub(r,s,t) 只是将t中第一次出现的r替换成s
# awk 'BEGIN {str="multiprocessors program"; sub(/pro/, "PRO", str); printf("%s\n", str)}'
// substr两个函数示例:
# awk 'BEGIN {str="multiprocessor programming"; print substr(str,6)}'
# awk 'BEGIN {str="multiprocessor programming"; print substr(str,6,9)}'
4.4.8 向awk脚本传递参数
格式为: awk脚本文件 param1=value1 param2=value2 输入文件
其中param可以是自定义变量,也可以是系统变量
注意:命令行参数不能被BEGIN中的语句访问到
4.4.9 条件语句和循环语句
这个跟C语言语法是一样的,不多做介绍了
# if (x==y) print x
# if (x ~ /[Hh]el?o) print x
while(cond) action
do action while(cond)
for (i=0; i<10; i++) print i
4.4.10 数组
array[index]=value
awk数组无需定义数组类型和大小,赋值后就能使用了,哦也。
1. 关联数组
关联数组其实就是一个Map,实际上awk数组就是一个Map,array[0]=12表示用字符串0为key,12为value
awk所有数组都是关联数组,array[09]跟array[9]完全不一样
基于上述的关联数组的含义,awk定义了一组foreach循环,而且很特殊的,跟java中还不一样:
for (var in array)
print array[var]
实际上var就相当于Map中的key,这样可以理解了吧,^_^
还有个条件表达式,相当于判断Map是否含有这个key值:
if (index in array) print "yes"
2. split函数
终于要到压轴大戏也就是大名鼎鼎的split函数了
split(r,s,t):将字符串r以t为分隔符拆分成字符串数组,存放在s中。 并返回数组s的大小
# awk 'BEGIN {print split("abc/def/ghi",str,"/")}'
# awk 'BEGIN {FS=","} {print split($1,name," ")}' input.txt
再来看一下一个小小脚本:
#!/bin/awk -f BEGIN {FS=","} { split($1,names," "); for (i in names) print name[i] }
3. 数组形式的系统变量
ARGV和ENVIRON两个
ARGC是ARGV数组中元素的个数
# awk -f argv.awk xyz n=99 "Hello World"
结果打印: awk,xyz,n=99,,Hello World
ENVIRON变量存储了Linux系统的环境变量
例如:
awk 'BEGIN {for(i in ENVIRON) print(i" = "ENVIRON[i])}'
4. awk 执行系统命令:
ls -l -c --time-style="+%Y-%m-%d" | awk '{if($6 == "2013-07-01") system("rm -rf " $7)}'
不小心在当前页面新建了很多文件,怎样马上删除呢?就是上面的例子
本人博客已搬家,新地址为:http://yidao620c.github.io/