简介
1.概述
awk、sed和grep,俗称Linux下的三剑客,它们之间有很多相似点,但是同样也各有个的特色,相似的地方是它们可以匹配文本,其中sed和awk还可以用于文本编辑,而grip则不具备这个功用。sed是一种非交互式且面向字符刘德编辑器(a “non-interactive” stream-oriented editor),而awk则是一门模式匹配的编程语言,因为它主要功能是用于匹配文本并处理,同时它有一些变成语言才有的语法,例如函数、分支循环语句、变量等等,当然比起我们常见的编程语言,awk相对比较简单。
awk是一个强大的文本分析工具,相对与grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大。简单来说awk就是把文件逐行的读入,以空格为默认分割符将每行切片,切开的部分再进行各种分析处理。
awk有3个不同版本: awk、nawk和gawk,未作特别说明,一般指gawk,gawk 是 AWK 的 GNU 版本。
awk其名称得自于它的创始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首个字母。实际上 AWK 的确拥有自己的语言: AWK 程序设计语言 , 三位创建者已将它正式定义为“样式扫描和处理语言”。它允许您创建简短的程序,这些程序读取输入文件、为数据排序、处理数据、对输入执行计算以及生成报 表,还有无数其他的功能。
2.原理
1)awk的输入被解析成多个记录(Record),默认情况下,记录的分割符是\n,因此可以认为一行就是一个记录,记录的分割符可以通过内置变量RS更改。档记录匹配某个pattern时,才会执行后续的action命令。
每个记录又进一步被分割成多个字段(Field),默认情况下字符的分割符是空白符,例如空格、制表符等,也可以通过-F ERE 选项或内置变量FS更改。在awk中,可以通过$1,$2,...来访问对应位置的字段,同时¥0存放整个记录。
2)awk基本结构包括模式匹配(用于找到要处理的行)和处理过程(即处理动作)
pattern {action}
说明:awk读取文件内容的每一行时,将对比该行是否与给定的模式相匹配,如果匹配则执行处理过程,否则对该行不做任何处理。
如果没有制定处理脚本,则把匹配的行显示到标准输出,即默认处理动作是print打印行;
如果没有制定模式匹配,则默认匹配所有数据。
3)awk有两个特殊的模式:BEGIN和END,它们被防止在没有读取任何数据之前以及在所有数据读取完成以后执行。
3.使用awk,可以做:
将文本文件视为由字段和记录组成的文本数据库
在操作文本数据库的过程中能够使用变量
能够使用数学运算和字符串操作
能够使用常见的变成结构,例如条件分支与循环
能够格式化输出
能够自定义函数
能够在awk脚本中执行UNIX命令
能够处理UNIX命令的输出结果
其他
使用方法
有三种方式调用awk
1.命令行方式
awk [-F field-separator] 'commands' input-file(s)
其中,commands 是真正awk命令,[-F域分隔符]是可选的。 input-file(s) 是待处理的文件。
在awk中,文件的每一行中,由域分隔符分开的每一项称为一个域。通常,在不指名-F域分隔符的情况下,默认的域分隔符是空格。
2.shell脚本方式
将所有的awk命令插入一个文件,并使awk程序可执行,然后awk命令解释器作为脚本的首行,一遍通过键入脚本名称来调用。
相当于shell脚本首行的:#!/bin/sh
可以换成:#!/bin/awk
3.将所有的awk命令插入一个单独文件,然后调用:
awk -f awk-script-file input-file(s)
其中,-f选项加载awk-script-file中的awk脚本,input-file(s)跟上面的是一样的。
命令行方式(其实就上上面的1/3):
awk [ -F ERE ] [ -v assignment ] ... program [argument]
awk [ -F ERE ] -f progfiles ... [ -v assignment ] ...[argument]
标准的awk命令行参数主要有以下三个:
-F ERE:定义字段分割符,该选项的值可以是扩展的正则表达式(ERE);
-f progfile:指定awk脚本,可以同时制定多个脚本,它们会按照在命令行中出现的顺序连接在一起;
-c assignment:定义awk变量,形式同awk中的变量赋值,即name=value,复制发生在awk处理文本之前。
简单示例:
1)指定分割符为冒号,并打印各个字段
$ echo "1:2:3" | awk -F ':' '{print $1 " and " $2 " and " $3}'
1 and 2 and 3
2)通过正则表达式“/^$”匹配空白行,动作为打印空行,文件中有几个空白行,就打印几个空白行
$ awk '/^$/' {print "Bland line"}' test.txt
3)打印包含主机名的行,因为没有制定动作指令,默认动作为打印
$ awk '/HOSTNAME/' /etc/sysconfig/network
4)在awk脚本中访问通过-v选项设置的变量
$echo | awk -v a=1 BEGIN {print a}'
1
5)提前编辑一个awk脚本,再通过-f选项调用该脚本
$ cat awk.sh
'/^$/ {print "Blank line"}'
$ awk -f awk.sh test.txt
awk基本操作
1. 记录与字段
awk一次从文件中读取一条记录,并将记录存储在字段变量$0中。记录被分割为字段并存储在$1,$2 ..., $NF中(默认使用空格或制表符为分隔符)。
内建变量NF为记录的字段个数
示例:
[jacob@localhost ~]# echo hello the world |
>awk '{print $1,$2,$3}'
备注:读取输入行并输出第一个字段,第二个字段,第三个字段。
[jacob@localhost ~]# echo hello the world |
>
备注:读取输入行并输出该行。
[jacob@localhost ~]# echo hello the world |
>awk '{print NF}'
备注:读取输入行并输出该行的字段个数:3个字段。
[jacob@localhost ~]# echo hello the world |
>awk '{print $NF}'
备注:读取输入行并输出该行的第三个字段,因为NF为3,所以$NF等同于取行的最后一个字段。
2. 字段分隔符
默认awk读取数据以空格或制表符作为分隔符,但可以通过-F或FS(field separator)变量来改变分隔符。
示例:
[jacob@localhost ~]# awk -F: '{print $1}' /etc/passwd
[jacob@localhost ~]# awk 'BEGIN {FS = ":"} {print $1}' /etc/passwd
备注:以上两个示例均将字段的分隔符改冒号(:),即以冒号为分隔符打印passwd文件的第一个字段(帐号名称)。
注意:如果使用FS改变分隔符的话,需要在BEGIN处定义FS,因为在读取第一行前就需要改变字段分隔符。
进阶:指定多个字段分隔符(文档内容为:hello the:word,!)
[jacob@localhost ~]# echo 'hello the:word,!' |
>awk 'BEGIN {FS="[:, ]"} {print $1,$2,$3,$4}'
3. 内置变量
以下为awk内置变量:
ARGC 命令行参数个数
FILENAME 当前输入文档的名称
FNR 当前输入文档的当前记录编号,尤其当有多个输入文档时有用
NR 输入流的当前记录编号
NF 当前记录的字段个数
FS 字段分隔符
OFS 输出字段分隔符,默认为空格
ORS 输出记录分隔符,默认为换行符\n
RS 输入记录分隔符,默认为换行符\n
示例:
[jacob@localhost ~]# cat test1.txt
This is a test file.
Welcome to Jacob's Class.
[jacob@localhost ~]#cat test2.txt
Hello the world.
Wow! I'm overwhelmed.
Ask for more.
[jacob@localhost ~]# awk '{print FNR}' test1.txt test2.txt
1
2
1
2
3
备注:输出当前文档的当前行编号,第一个文件两行,第二个文件三行。
[jacob@localhost ~]# awk '{print NR}' test1.txt test2.txt
1
2
3
4
5
备注:awk将两个文档作为一个整体的输入流,通过NR输入当前行编号。
[jacob@localhost ~]# awk '{print NF}' test1.txt
5
4
备注:test1.txt文档的第一行有5个字段,第二行有4个字段。
[jacob@localhost ~]# awk 'BEGIN {FS = ":"} {print $1}' /etc/passwd
[jacob@localhost ~]# awk '{print $1,$2,$3}' test1.txt
备注:默认print输出时,各参数将的输出分隔符默认为空格,所以输出内容如下
This is a
Welcome to Jacob's
[jacob@localhost ~]# awk 'BEGIN {OFS="-"} {print $1,$2,$3}' test1.txt
备注:通过OFS将输出分隔符设置为"-",这个print在输出第一、二、三个字段时,中间的分隔符为"-",结果如下
This-is-a
Welcome-to-Jacob's
[jacob@localhost ~]# cat test3.txt
mail from: [email protected]
subject:hello
data:2012-07-12 17:00
content:Hello, The world.
mail from: [email protected]
subject:congregation
data:2012-07-12 08:31
content:Congregation to you.
mail from: [email protected]
subject:Test
data:2012-07-12 10:20
content:This is a test mail.
[jacob@localhost ~]# awk 'BEGIN {FS="\n"; RS=""} {print $3}' test3.txt
备注:读取输入数据,以空白行为记录分隔符,即第一个空白行前的内容为第一个记录,第一个记录中字段分隔符为换行符。
以上awk的效果为打印所有的邮件时间,即每个记录的第三个字段。
4. 表达式与操作符
表达式是由变量、常量、函数、正则表达式、操作符组成,awk中变量有字符变量和数字变量。如果在awk中定义的变量没有初始化,则初始值为空字串或0。
注意:字符操作时一定记得需要加引号
1) 变量定义示例:
a="welcome to beijing"
b=12
2) 操作符(awk操作符与C语言类似)
+ 加
- 减
* 乘
/ 除
% 取余
^ 幂运算
++ 自加1
-- 自减1
+= 相加后赋值给变量(x+=9等同于x=x+9)
-= 相减后赋值给变量(x-=9等同于x=x-9)
*= 相乘后赋值给变量(x*=9等同于x=x*9)
/= 相除后赋值给变量(x/=9等同于x=x/9)
> 大于
< 小于
>= 大于等于
<= 小于等于
== 等于
!= 不等于
~ 匹配
!~ 不匹配
&& 与
|| 或
操作符简单示例:
[jacob@localhost ~]# echo "test" | awk 'x=2 {print x+3}'
[jacob@localhost ~]# echo "test" | awk 'x=2,y=3 {print x*2, y*3}'
[jacob@localhost ~]# awk '/^$/ {print x+=1}' test.txt 备注:统计所有空白行
[jacob@localhost ~]# awk '/^$/ {x+=1} END {print x}' test.txt 备注:打印总空白行个数
[jacob@localhost ~]#
awk -F: '$1~/root/ {print $3}' /etc/passwd 备注:打印root的ID号
[jacob@localhost ~]#
awk -F: '$3>500 {print $1}' /etc/passwd 备注:列出计算机中ID号大于500的用户名
awk条件及循环语句
1. IF条件判断格式:
if (表达式)
动作1
else
动作2
或者if (表达式) 动作1;else 动作2
说明:如果表达式的判断结果为真则执行动作1,否则执行动作2。
示例:(判断boot分区可用容量小于20M时报警,否则显示OK)
[jacob@localhost ~]# df |grep "boot" |awk '{if($4<20000) print "Alart"; else print "OK"}'
2. 循环
while (条件)
动作
x=1
while (i < 10) {
print $i
}
示例:
[jacob@localhost ~]# awk 'i=1 {} BEGIN { while (i<=10) {++i; print i}}' test.txt
do
动作
while (条件)
示例:
[jacob@localhost ~]# awk 'BEGIN { do {++x;print x} while (x<=10)}' test.txt
for (变量;条件;计数器)
动作
示例:
for (i=1;i<=10;i++)
print i
[jacob@localhost ~]# awk 'BEGIN {for(i=1;i<=10;i++) print i}' test.txt
[jacob@localhost ~]# awk 'BEGIN {for(i=10;i>=1;i--) print i}' test.txt
说明:因为以上循环语句使用的awk均使用的BEGIN模式,所以输入文档可以为任意文档(无关紧要)。
3. Break与Continue
break 跳出循环
continue 终止当前循环
示例:
for (i=1; i<=10;i++) {
if (i=5)
continue
print i
}
说明:打印1-4,6-10
for (i=1; i<=10;i++) {
if (i=5)
break
print i
}
说明:仅打印1-4
主要参考博文:“丁丁历险” http://manual.blog.51cto.com/3300438/932958