入门第一讲:awk调用方法
awk语句都是由模式(pattern)和动作(action)组成
例1.awk三种调用方式整理
①命令行调用
1.新建一个文件input
2.输入2个回车,保存
3.做正则匹配打入如下命令awk '/^$/{print "this is a blank line."}' input
[zhangyongqiao.pt@v128217:~$] cat input
[zhangyongqiao.pt@v128217:~$] awk '/^$/{print "this is a blank line."}' input
this is a blank line.
this is a blank line.
注:模式:/^$/ 的正则表达式匹配的是空白行
动作:print "this is a blank line."
②脚本间接调用
1.新建文件src.awk 输入如下cat到的内容
2.使用awk -f 调用src.awk文件
[zhangyongqiao.pt@v128217:~$] cat src.awk
/^$/{print "this is a blank line."}
[zhangyongqiao.pt@v128217:~$] awk -f src.awk input
this is a blank line.
this is a blank line.
注:这里的awk -f src.awk 命令段相当于awk '/^$/{print "this is a blank line."}'
③脚本直接调用
1.修改src.awk文件,修改内容参照cat到的部分
2.直接运行./src.awk + 待读入文件名
[zhangyongqiao.pt@v128217:~$] cat src.awk
#!/bin/awk -f #用了那么久,今天终于知道“#!”叫“sha-bang符号”
/^$/{print "this is a blank line."}
[zhangyongqiao.pt@v128217:~$] ./src.awk input
this is a blank line.
this is a blank line.
注:这里的 ./src.awk 命令段相当于awk '/^$/{print "this is a blank line."}'
入门第二讲:记录和域
文件的一行称为一条记录,一条记录的每一个字符串称为一个域,域以空格。TAB及其他符号(, . 、等)分割。
例2:记录和域的一些常用方式
首先编辑input文件如下:#小空格为空格键,大空格为TAB键
对第一条记录做解释:zhang yi 001 1989 分别为第一条记录对应的4个域
=======================================================
[zhangyongqiao.pt@v128217:~$] cat input
zhang yi 001 1989
zhang er 002 1990
zhang san 003 1991
========================================================
①利用域操作符$对文件做简单的处理
[zhangyongqiao.pt@v128217:~$] awk '{print $1,$2,$3}' input
zhang yi 001
zhang er 002
zhang san 003
②配合变量运算表达式对文件处理
[zhangyongqiao.pt@v128217:~$] awk 'BEGIN {A=1} {print $(1+A)}' input
yi
er
San
################################################################
这里引入一个关于BEGIN的解释
用法:BEGIN后面跟着的一个表达式在文件读入之前执行一次
[zhangyongqiao.pt@v128217:~$] awk 'BEGIN {A=0}{A++} {print $(1+A)}' input
yi #A=1
002 #A=2
1991 #A=3
[zhangyongqiao.pt@v128217:~$] awk 'BEGIN {A=0;A++} {print $(1+A)}' input
yi #A=1
er #A=1
san #A=1
第一条命令{A=0}在读入文件之前执行了一次,后面的命令在每行循环时重复执行
第二条命令{A=0;A++}在读入文件之前执行了一次,后面的命令在每行循环时重复执行
注:awk循环原理与sed一样,可以参见上一篇sed入门的最后一部分
################################################################
③使用FS变量改变分隔符
首先对文本进行一点小改动,将所有的空格和TAB换成逗号
[zhangyongqiao.pt@v128217:~$] cat input
zhang,yi,001,1989
zhang,er,002,1990
zhang,an,003,1991
[zhangyongqiao.pt@v128217:~$] awk 'BEGIN {FS=","} {print $2}' input
yi
er
An
****************************************************************
这里大家都知道了$1,$2...的意义了,那么$0又是什么呢?演示一下:
[zhangyongqiao.pt@v128217:~$] awk 'BEGIN {FS=","} {print $0}' input
zhang,yi,001,1989
zhang,er,002,1990
zhang,an,003,1991
这下不用解释也知道了吧。$0表示的就是一行记录里面所有的域
****************************************************************
入门第三讲:关系和布尔运算符
讲在前面:
这里就不列举常用布尔运算符了(< > = !=....)
2个比较特色的运算符:
1.~ 匹配正则表达式
2.!~不匹配正则表达式
例3.正则表达式与运算符的使用举例
首先看一个系统配置文件~~~~~~~嘿嘿。存密码的哦~
################################################################
[zhangyongqiao.pt@v128217:~$] cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
……
……
……
################################################################
①使用正则表达式~
#第一个域匹配root的
[zhangyongqiao.pt@v128217:~$] awk 'BEGIN {FS=":"} $1~/root/' /etc/passwd
root:x:0:0:root:/root:/bin/bash
#所有域匹配root的
[zhangyongqiao.pt@v128217:~$] awk 'BEGIN {FS=":"} $0~/root/' /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
#所有域不匹配nologin的
[zhangyongqiao.pt@v128217:~$] awk 'BEGIN {FS=":"} $0~/nologin/' /etc/passwd
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
②使用if语句等
观察文件第三个域与第四个域 做如下尝试
[zhangyongqiao.pt@v128217:~$] awk 'BEGIN {FS=":"} {if($3==$4) print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
nscd:x:28:28:NSCD Daemon:/:/sbin/nologin #省略后面的部分
当然,if也可以如其他编程语言规则一样进行多条件匹配
[zhangyongqiao.pt@v128217:~$] awk 'BEGIN {FS=":"} {if ($3==$4&&$4==0) print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
[zhangyongqiao.pt@v128217:~$]#
注:运算符基本操作就是如此了,其他的就不再特意举例说明
入门第四讲:表达式
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
表达式区别于布尔运算符
所谓表达式最典型的就是(int)a+(int)b=(int)c的操作
而布尔运算指的是a<b之类的操作,返回值为0或者1
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
例4.表达式1例
①简单的除法运算
[zhangyongqiao.pt@v128217:~$] cat input
zhang,yi,001,1989
zhang,er,002,1990
zhang,an,003,1991
[zhangyongqiao.pt@v128217:~$] awk 'BEGIN {FS=","} {a=$4; b=a/2; print b}' input
994.5
995
995.5
入门第五讲:系统变量
关于系统变量,这里举出几个例子,详细的表格可以参看一些专门的文档
……………………………………………………………………………………
变量名 意义
$0 记录的所有域
$n 当前记录的第n个域
FILENAME 当前使用的文件名
NF 当前记录中域的数量
NR 当前记录数
……………………………………………………………………………………
例5.对如上提到的一些系统变量做实践
[zhangyongqiao.pt@v128217:~$] cat input
zhang,yi,001,1989
zhang,er,002,1990
zhang,an,003,1991
[zhangyongqiao.pt@v128217:~$] awk 'BEGIN {FS=","} {print NF,NR,$0} END{print FILENAME}' input
4 1 zhang,yi,001,1989
4 2 zhang,er,002,1990
4 3 zhang,an,003,1991
入门第六讲:格式化输出
语法规则基本上等同于c语言的printf使用
基本语法如下:
Printf(格式控制符,参数)
###############################################################
所谓的格式控制符也就是(举例说明,不全)
格式符 意义
%c 字符
%s 字符串
%d 整型
%e 浮点数(科学记数)
%f 浮点数
%s 字符串
################################################################
例6.基本用法举例
对同一种输出,给出三种不同的命令
[zhangyongqiao.pt@v128217:~$] awk 'BEGIN {FS=","} {printf("%s%d",$1,$4)}' input
zhang1989zhang1990zhang1991[zhawk 'BEGIN {FS=","} {printf("%s%d\n",$1,$4)}' input
zhang1989
zhang1990
zhang1991
[zhangyongqiao.pt@v128217:~$] awk 'BEGIN {FS=","} {printf("%s\t%d\n",$1,$4)}' input
zhang 1989
zhang 1990
zhang 1991
注:第一条命令在行尾缺少\n,不进行换行,导致第二条命令直接接在第一条命令的输出之后。
那么printf与print的区别何在呢?我们来看下面2行命令的输出
[zhangyongqiao.pt@v128217:~$] awk 'BEGIN {FS=","} {printf("%s\t%d\n",$1,$4)}' input
zhang 1989
zhang 1990
zhang 1991
[zhangyongqiao.pt@v128217:~$] awk 'BEGIN {FS=","} {print("%s\t%d\n",$1,$4)}' input
%s %d
zhang 1989
%s %d
zhang 1990
%s %d
zhang 1991
通过输出对比,很容易看出,对于制表符(\t)的处理2者是一样的,但是对于格式符,是printf特有的,在print的基本语法中是不包含这些格式符的。因此他们不能被识别,只能原样输出。
————————————————————————————————
注意:对于第2条命令的2,4,6行,
这三行前面都有空格符,这个是怎么回事呢?
看命令行对比:【注意,这里是print,而非printf】
[zhangyongqiao.pt@v128217:~$] awk 'BEGIN {FS=","} {print("",$1,$4)}' input
zhang 1989
zhang 1990
zhang 1991
[zhangyongqiao.pt@v128217:~$] awk 'BEGIN {FS=","} {print("" $1,$4)}' input
zhang 1989
zhang 1990
zhang 1991
So,在print中,双引号直接被识别成一个字段,这个空格是因为逗号引起的
————————————————————————————————
入门第七讲:内置字符串函数
按照惯例,先列举一些常用的函数
****************************************************************
函数名 意义
gsub(r,s) 在输入文件中s替换r
gsub(r,s,t) 在t字符串中用s替换r
index(s,t) 取出t字符串中s所在的位置
length(s) s字符串的长度
match(s,t) t字符串中是否有s
split(r,s,t) 在t上将r分成序列s
sub(r,s,t) 将t中第一次出现的r替换成s
substr(r,s,t) 返回r中从s开始的后缀部分
sbustr(r,s,t) 返回字符串r中从s开始长度为t的后缀部分
***************************************************************
例7.关于内置字符串函数的使用
[zhangyongqiao.pt@v128217:~$] awk 'BEGIN {print index("abcdef","a")}'
1
简单的,在abcdef字符串中找到a字符的位置,也就是第一位
注意:这里的index与JAVA中的indexof使用有区别,java中数组下标为0的位为第一位,而这里第一位就是字符串的第一个字符所在的位置。
========================================================
对BEGIN的一些强化
[zhangyongqiao.pt@v128217:~$] awk 'BEGIN {print index("abcdef","a")}'
1
[zhangyongqiao.pt@v128217:~$] awk '{print index("abcdef","a")}'
1
1
从上面2段命令对比可以很显然看到区别,对于BEGIN的定义是在文档读入前进行一次操作。因此不给出文档名,整段print语句也能执行
那么,对于第二条命令,awk命令一直在等待输入一个文档,然后对文档的每一行进行print语句操作。
========================================================
入门第八讲:向awk脚本传递参数
脚本内的变量可以在命令行中进行赋值,实现向awk脚本传递参数
例8.在脚本中使用命令行的参数
[zhangyongqiao.pt@v128217:~$] cat output.awk
#!/bin/awk -f
{print ("The line number is "NR" ,Define is "Define"" )}
[zhangyongqiao.pt@v128217:~$] ./output.awk Define=3 FS="," input
The line number is 1 ,Define is 3
The line number is 2 ,Define is 3
The line number is 3 ,Define is 3
这一段没有特别需要注意的,将脚本语句转到命令行中就很简单了。
以上的操作在命令行中也就是:
[zhangyongqiao.pt@v128217:~$] awk 'FS=",",Define=3 {print ("The line number is "NR" ,Define is "Define"" )} ' input
The line number is 1 ,Define is 3
The line number is 2 ,Define is 3
The line number is 3 ,Define is 3
一个小问题:希望得到解决
————————————————————————————————
[zhangyongqiao.pt@v128217:~$] awk 'Define=3 {print ("The line number is "NR" ,Define is "Define"" )} ' input
The line number is 1 ,Define is 3
The line number is 2 ,Define is 3
The line number is 3 ,Define is 3
[zhangyongqiao.pt@v128217:~$] awk 'Define=3 {print ("The line number is "NR" ,Define is "Define"" )} FS=","' input
The line number is 1 ,Define is 3
zhang,yi,001,1989
The line number is 2 ,Define is 3
zhang,er,002,1990
The line number is 3 ,Define is 3
zhang,an,003,1991
————————————————————————————————
为什么上面3条类似的命令打印出来的结果却不一样呢?
入门第九讲:简单的条件语句与循环语句
循环和条件语句与一般语言类似,这里就不展开讨论了。给出例9
例9.简单的条件判断语句
[zhangyongqiao.pt@v128217:~$] awk 'BEGIN{i=1} {if(i==NR){print"The line number is "NR""} i++}' input
The line number is 1
The line number is 2
The line number is 3
入门第十讲:数组
数组的基本格式:array[index]=value
区别:在awk中,无需定义数组的大小,不像c语言那样,以连续的地址存储。Awk的数组称之为关联数组,类似于key-value的形式,也就是说index的位置可以是小数或者是字符串
例9.简单的数组应用
①基本命令
[zhangyongqiao.pt@v128217:~$] awk 'BEGIN{data[10.1]="120";;printf("%s\n",data[10.1])}'
120
[zhangyongqiao.pt@v128217:~$] awk 'BEGIN{data[10]="120";;printf("%s\n",data[10])}'
120
[zhangyongqiao.pt@v128217:~$] awk 'BEGIN{data['a']="120";;printf("%s\n",data['a'])}'
120
上述三个命令很好的解释了awk的数组与一般编程语言中数组的区别。
②判断一个数组元素是否存在的方式
[zhangyongqiao.pt@v128217:~$] awk 'BEGIN{data['a']="120"; if('a' in data) print"ok"}'
ok
③split函数应用
Split(r,s.t)将r字符串以t字符串划分,结果存入s数组中
[zhangyongqiao.pt@v128217:~$] cat input
zhang,yi,001,1989
zhang,er,002,1990
zhang,an,003,1991
[zhangyongqiao.pt@v128217:~$] awk 'BEGIN {FS=","} {split($1,name,"n"); for (i in name) print name[i]}' input
zha
g
zha
g
zha
g
对于$1也就是字符串“zhang”的操作,以“n”为分隔符,对这个字符串划分,然后打印,分隔符不打印
④数组形式的系统变量
ARGV储存命令行参数,ARGC为ARGV中参数的个数,下面的命令直观的体现了他们的用法。
[zhangyongqiao.pt@v128217:~$] awk 'BEGIN{for(x=0;x<ARGC;x++)print ARGV[x];print ARGC}' xyz n=99 "hello"
awk
xyz
n=99
hello
4
到这里,sed和awk编程的入门就结束了,sed用于刘编辑,将一系列的编辑命令作用于缓冲区中的输入文件的副本,从而实现了对输入文件的各种编辑操作。而awk的一大显著特点是处理结构化文件,所谓的结构化文件,是指划分为记录和与的文件,并且awk提供printf语句能生成格式化报表。
这里分享一本经典的sed与awk深入书籍:
O'reilly&Associcates于2000年出版的由Arnold Robbin 编著的sed&awk Pocket Peference