Shell文本处理三剑客之一awk(1)
AWK是一种优良的文本处理工具。它不仅是 Linux 中 也是任何环境中现有的功能最强大的数据处理引擎之一。 这种编程及数据操作语言(其名称得自于它的创始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首个字母)的最大功能取决于一个人所拥有的知识。 AWK 提供了极其强大的功能:可以进行样式装入、流控制、 数学运算符、进程控制语句甚至于内置的变量和函数。 它具备了一个完整的语言所应具有的几乎所有精美特性。 实际上 AWK 的确拥有自己的语言:AWK 程序设计语言, 三位创建者已将它正式定义为“样式扫描和处理语言”。 它允许您创建简短的程序,这些程序读取输入文件、 为数据排序、处理数据、对输入执行计算以及生成报表,还有无数其他的功能。
最简单地说, AWK 是一种用于处理文本的编程语言工具。
AWK 在很多方面类似于 shell 编程语言,
尽管 AWK 具有完全属于其本身的语法。
它的设计思想来源于 SNOBOL4 、sed 、Marc Rochkind设计的有效性语言、语言工具 yacc 和 lex ,
当然还从 C 语言中获取了一些优秀的思想。在最初创造 AWK 时,其目的是用于文本处理,
并且这种语言的基础是,只要在输入数据中有模式匹配,就执行一系列指令。
该实用工具扫描文件中的每一行,查找与命令行中所给定内容相匹配的模式。
如果发现匹配内容,则进行下一个编程步骤。如果找不到匹配内容,则继续处理下一行
awk被认为是一种最难掌握的shell文本处理工具
awk程序设计语言,“样式扫描和处理语言”,它允许您创建间短的程序,这些程序读取输入文件,
为数据排序、处理数据、对输入执行计算及生成报表,还有无数其他功能
掌握了awk将会使你的工作变的高大上
awk程序由一个主输入循环(Main input loop)维持,主输入循环反复执行,直到条件被触发,
主循环无需由程序员去写,awk已经搭好主输入循环的框架
awk编程模型(读取文件,操作文件,这个过程是自动的):
第一阶段:读取输入文件之前,由BEGIN 第二阶段:主输入循环,对每个输入文件进行处理 第三阶段:读取输入文件完毕,由END标识 程序员写的代码被嵌入到主输入循环中框架中执行,主输入循环自动依次读取输入文件行处理,
处理文件行的动作是由程序员来添加的
其他编程语言,如C,C++,JAVA等,程序员需要写一个main函数然后打开文件,
读取文件,进行相应的处理,关闭文件,这些操作,在awk,我们不用做,awk中自动帮我们完成这些步骤
awk定义两个特殊的字段
BEGIN 用于在主输入循环之前执行(在未读取输入文件之前执行)
END 用于在主输入循环结束后执行
BEGIN和END在中间的循环之外执行的,一个是开始,一个是结束
awk的基础语法:
- awk模式匹配(直接将模式和动作写在终端里面 后面接一个文件)
任何awk语句都由模式(pattern)和动作(action)组成模式是由一组用于测试输入行是否需要执行动作的规则动作是包含语句,函数和表达式的执行过程,
即模式决定动作何时触发和触发事件动作执行对输入行的处理
(1)直接使用命令
[root@localhost awk]# awk '/^$/{print "This is a blank line."}' input
单引号之间是awk命令,
awk命令由两部分组成,
/ / 包围的东西叫做模式 (即使用/来将他和第二部分分隔)
{}里面的是动作
input是处理的文件
命令表示一旦读如的文件输入行是空行,就打印This is a blank line.(按行处理)
[root@localhost awk]# cat input
#这里是空白行不是没有东西
[root@localhost awk]# awk '/^$/{print "This is a blank line."}' input
This is a blank line.
This is a blank line.
This is a blank line.
(2)可以把awk的命令(两部分缺一不可)写在一个文件里面,我们去调用它
Awk命令文件的后缀没有要求,但是我们为了遍于区分将后缀写成awk
调用的命令 :awk -f awk命令文件 处理文件
[root@localhost awk]# cat scr.awk
/^$/{print "This is a blank line."}
[root@localhost awk]# awk -f scr.awk input
This is a blank line.
This is a blank line.
This is a blank line.
(3)脚本调用awk操作
[root@localhost awk]# which awk
/usr/bin/awk
[root@localhost awk]# ll -l /usr/bin/awk
lrwxrwxrwx. 1 root root 4 Jan 29 21:18 /usr/bin/awk -> gawk
[root@localhost awk]# ll -l /usr/bin/gawk
-rwxr-xr-x. 1 root root 428576 Jan 25 2014 /usr/bin/gawk
[root@localhost awk]# cat scr.awk
#!/usr/bin/awk -f
/^$/{print "This is a blank line."}
[root@localhost awk]# ./scr.awk input
This is a blank line.
This is a blank line.
This is a blank line.
- 记录和域
Awk认为输入文件是结构话的,awk将每个输入文件行定义为记录,
行中的每个字符串定义为域,域之间用空格,Tab键或其他符号进行分隔,
分隔域的符号叫做分隔符
#awk定义域操作符$来指定动作的域,域操作符$后面跟数字或变量来标识域的位置
,每条记录的域从1开始编号,如$1表示第一个域$0表示所有域
如下有一条记录
Wang Wu 058-25454420
↓ ↓
空格符 Tab键
两个或多个空格键或者Tab键可以作为一个分隔符来处理
对文本进行分域处理是linux系统中很多命令都会用到的
比如说sort和uniq和join和cut都用了域和分隔符的概念
[root@localhost awk]# cat stucode
Tom Li 028-123456789 xian
Tom Li 028-123456789 xian
Tom Li 028-123456789 xian
我们可以看到这个文件中的每条记录分为4个域(一个或多个空格或者Tab键都算是分隔符相当于同一个东西)
awk '{print $2,$1,$4,$3}' stucode
[root@localhost awk]# awk '{print $2,$1,$4,$3}' stucode
Li Tom xian 028-123456789
Li Tom xian 028-123456789
Li Tom xian 028-123456789
[root@localhost awk]# awk '{print $0}' stucode
Tom Li 028-123456789 xian
Tom Li 028-123456789 xian
Tom Li 028-123456789 xian
$后还可以跟变量或变量运算表达式
$[root@localhost awk]# awk 'BEGIN {one=1;two=2} {print $(one+two)}' stucode #打印出第三个域
028-123456789
028-123456789
028-123456789
BEGIN是在完成操作前要完成的操作,给BEGIN里面的定义一个动作,
{one=1;two=2} #{}里面放的是动作,将动作用分号;分开,模式使用单引号,单引号里面有很多动作
我们的动作可以是print打印
打印后面可以跟表达式(或变量,shell中的变量前面只有$,表达式前面有$(表达式)) 所以这里的时表达式,里面是变量运算(里面变量不加$)
awk使用域概念处理时,tab键会当连续的空格处理
[root@localhost awk]# cat stucode
Tom Li 028-123456789 xian
Tom Li 028-123456789 xian
Tom Li 028-123456789 xian
[root@localhost awk]# awk 'BEGIN {one=1;two=2} {print $(one+two)}' stucode
028-123456789
028-123456789
028-123456789
#我们发现结果没有改变
awk -F "\t"
(1)# -F指定域的分隔符,后面用双引号将分隔符包围,这里使用的是制表符Tab键
[root@localhost awk]# cat stucode
Tom Li 028-123456789 xian
Tom Li 028-123456789 xian
Tom Li 028-123456789 xian
Tom和Li之间有两个制表符
所以$1是Tom
[root@localhost awk]# awk -F "\t" '{print $2}' stucode
#显示$2为空因为,两个制表符之间为空(分隔符是Tab键)
[root@localhost awk]# awk -F "\t" '{print $3}' stucode
Li 028-123456789 xian
Li 028-123456789 xian
Li 028-123456789 xian
我们使用Tab做分隔符的时候,不要两个Tab连在一起(这样容易出现空的域)
解决方法:使用正则表达式+表示匹配一个到多个(这样就不会出现空的域了)
[root@localhost awk]# awk -F "\t+" '{print $1}' stucode
Tom
Tom
Tom
[root@localhost awk]# awk -F "\t+" '{print $2}' stucode
Li 028-123456789 xian
Li 028-123456789 xian
Li 028-123456789 xian
[root@localhost awk]# cat stucode
Tom Li 028-123456789 xian
Tom Li 028-123456789 xian
Tom Li 028-123456789 xian
- awk的环境变量FS,我们可以通过在BEGIN字段中设置FS的值来改变分隔符
FS的值被双引号””包围
[root@localhost awk]# awk 'BEGIN {FS=","} {print $0}' stucode
Tom,Li,028-123456789,xian
Tom,Li,028-123456789,xian
Tom,Li,028-123456789,xian
[root@localhost awk]# awk 'BEGIN {FS=","} {print $1,$3}' stucode
Tom 028-123456789
Tom 028-123456789
Tom 028-123456789
- awk关系和布尔运算
awk定义了一组关系运算符用于awk模式匹配
运 算 符
意 义
<
小于
>
大于
<=
小于等于
>=
大于等于
==
等于
!=
不等于
~
匹配正则表达式
!~
不匹配正则表达式
awk 'BEGIN {FS=":"} $1~/root/ 匹配第一个域里面有root的行,$1是第一个域
[root@localhost awk]# awk 'BEGIN {FS=":"} $1~/root/' passwd
root:x:0:0:root:/root:/bin/bash
所有域中至少有一个域为root的行
[root@localhost awk]# awk 'BEGIN {FS=":"} $0~/root/' passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
全部域不匹配nologin(所有域都没有nologin的行)
[root@localhost awk]# awk 'BEGIN {FS=":"} $0!~/nologin/' passwd
root:x:0:0:root:/root:/bin/bash
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
huige:x:1000:1000:huige:/home/huige:/bin/bash
student:x:1001:1001::/home/student:/bin/bash
awk有条件语句if,if后面的条件用小括号括起来if(条件)后面可以直接跟动作(表示满足条件执行该动作),条件可根据上面表格来写
[root@localhost awk]# awk 'BEGIN {FS=":"} {if ($3<$4) print$0}' passwd
adm:x:3:4:adm:/var/adm:/sbin/nologin #第3个域小于第4个域的行
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
多条件精确匹配
awk 'BEGIN {FS=":"} {if ($3==1||$4==10) print$0}'
#匹配以:为分隔符,第3个域是1或者第4个域是10,&&表示与(且)关系
[root@localhost awk]# cat huige
huige:niubi:1:fdsfgrh:fhdsjm
huijie:gengniubi:1:10:fdsfsdjkf
jfdsklj:fdsfjlk:kfldsjfkl:10:fdjshgjfks
[root@localhost awk]# awk 'BEGIN {FS=":"} {if ($3==1||$4==10) print$0}' huige
huige:niubi:1:fdsfgrh:fhdsjm
huijie:gengniubi:1:10:fdsfsdjkf
jfdsklj:fdsfjlk:kfldsjfkl:10:fdjshgjfks
[root@localhost awk]# awk 'BEGIN {FS=":"} {if ($3==1&&$4==10) print$0}' huige
huijie:gengniubi:1:10:fdsfsdjkf