《Linux Shell》之三:awk编程

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://www.pycoding.com/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值