AWK编程
简介:awk有很多内容,我没精力去全搞定它,所以该文也只是主要介绍大部分的内容,所以如有不足的地方,还是google把,本人也是shell的菜鸟一名,各位大神勿喷……
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
(awk的身世是很霸气的)介绍:
awk 是一种优秀的文本处理工具(或者可以称为文本处理的计算机语言),在类 Unix 系统中,它是功能最全、最强大的文本数据处理引擎之一。和它优异的性能相比,其知名度还远没有达到其应有的高度。(性能很好!!!)
awk 的名称
awk 的名称本身没有特殊的意义,来源于贝尔实验室计算机科学研究中心三位创始人(Alfred Aho(阿尔弗雷德.艾候)、Peter Weinberger(彼得.温伯格)、Brian Kernighan(布莱恩.柯林汉))姓氏的首字母。
awk 第一次出现是偶然的,研究中心的另外一位研究人员看到三位创始人在同一个房间里面讨论字符串匹配算法,就喊道 "awk! awk!",于是三位创始人就把他们正在开发的模式匹配工具称为 awk。
注释:awk 音同 auk(海雀)。
awk 出现的背景
和大部分编程语言一样,awk 是应实际需要而生的。20 世纪 80 年代,贝尔实验室计算机科学研究中心的研究员为了处理一些文本数据,开发了针对简易数据处理的模式匹配语言 - awk。
awk 在开发之初借鉴了 grep 匹配模式-执行动作的思想,同时扩展了 grep 在文本处理上的局限。在 awk 出来之后,由于其简洁的语法和强大的功能得到了广泛的使用,成为了类 Unix 系统的标准功能,并深刻影响了新生的语言。10 年之后出现的 Perl 语言,便借鉴了 awk 的特性。
注释:在 awk 中使用了富于表达的记法和高效的字符串匹配算法,执行效率非常高,其是理论与实践结合的典范。最好的工程实践往往是建立在科学的基础上。
awk 的功能
awk 是一种处理文本文件的计算机语言。它把文件看作是一些记录(record)的集合,默认情况下一行文本就是一个记录。每一行文本又被拆分成若干域(field)。我们可以把一个文本行的第一个词看作第一个域,第二个词看作第二个域,以此类推。
一个 awk 程序就是多个 "模式-动作"(pattern - action) 语句的集合。awk 一次读入一行文本,然后使用程序中的各个模式进行扫描,一旦匹配成功就执行相应的动作。
awk 经过多次修改,在 20 世纪 80 年代中期完成了主要功能,形成了一门计算机语言的雏形。实现了变量、操作符、流程控制、函数等成为计算机语言的基本元素。之后三位创始人将 awk 正式定义为模式扫描和处理语言(pattern scanning and processing language)。
参考网站
http://zh.wikipedia.org/wiki/AWK
1 awk记录和域:
解释:awk认为输入文件是结构化的,awk将每个输入文件行定义为记录,行中的每个字符串定义为域,域之间用空格、Tab键或其他符号进行分割,分割域的符号就叫分隔符(默认分割符是空格!!!!)
(1)eg :
sturecord文件:
zhangsan 22 beijing 010
lisi 23 wuhan 027
wangwu 23 shenyang 024
此处分别有四个域 ,分割符是空格
在终端执行: awk '{print $2,$1,$4,$3}' file1
结果:
22 zhangsan 010 beijing
23 lisi 027 wuhan
23 wangwu 024 shenyang
($1 代表第一域! $2第二 依次类推,而 $0 代表所有 即全域!!!!!)
eg : awk '{print $0}' file1
结果:
zhangsan 22 beijing 010
lisi 23 wuhan 027
wangwu 23 shenyang 024
AWK BEGIN & END
begin 和 end 模块是 awk 提供的一个特色功能,它们是两个非常特殊的模块(有别于 action 模块)。这个功能深刻的影响了后来出现的 perl 语言。
begin 模块是在任何文件被读入之前执行的模块;而 end 模块是在任何输入文件被处理完成之后执行的模块。
begin 和 end 模块语法格式如下:
BEGIN {
statement;
...
}
END {
statement;
...
}
1 关键字 BEGIN 和 END 后必须使用一个左大括号。
2 在一个 awk 程序中,可以使用多个 begin 和 end 模块,它们会按照顺序以此执行(先出现的先执行)。
3 begin 模块是在 awk 处理任何输入文件之前执行的,故常用于给内置变量赋值。
4 end 模块是在 awk 处理完所有的输入文件之后执行的,故常可以对 action 的操作结果进行处理。eg
脚本:两个开始模块 两个end模块
#!/usr/bin/awk -f
BEGIN{
print "first begin module";
}
BEGIN{
print "scond begin module";
}
{
print "action module";
}
END{
print "first end module";
}
END {
print "second end module;"
}
执行:
2 awk修改分割符 两种方式: -F , FS变量anders@AndersPC:~/code/shell/awk$ ./beginend.sh file1
first begin module
scond begin module
action module
action module(为什么会输出两个 action module 因为 file1文件中 有两行内容, awk是按照行读得 所以……)
first end module
second end module;
(1)-F 例子
eg :
file内容为:
zhang san 21beijing 010
li si 23 wuhan 027
wang wu 22 chengdu 024
(只有姓名之间是空格, 其余之间是tab,默认情况下空格是分割符 所以这样只有两个域,第一域 zhang 第二域: san 21 beijing 010)
如果要以tab为分割符,则 zhang san 这样被看作是一个域
命令: awk -F"\t" '{print $2}' file2
结果:
21
23
22
(2)FS变量:
文件内容:
zhang san,21,beijing,010
li si,23,wuhan,027
wang wu,22,chengdu,024
eg : awk 'BEGIN {FS=","} {print $2,$3}' file3
结果:
21 beijing
23 wuhan
22 chengdu
(记住:awk 以 域和记录来处理文件的 , sed处理的是流!!!!!!!!)
3 awk关系、布尔运算符、表达式:
运算符 意义;
< 小于
> 大于
<= 小于等于
>= 大于等于
== 等于
!= 不等于
~ 匹配正则表达式
!~ 不匹配正则表达式
|| 逻辑或
&& 逻辑与
! 逻辑非
与其他编程语言一样,awk表达式用于存储、操作和获取数据,一个awk表达式可由数值、字符常量、变量、操作符、函数和正则表达式自由组合而成
变量是一个值的标识符,定义awk变量非常方便,只需定义一个变量名并将值赋给它即可。变量名只能包含字母、数字和下划线,而且不能以数字开头
运算符 意义
+ 加
- 减
* 乘
/ 除
% 模
^或** 乘方
++x 在返回x值之前,x变量加1
x++ 在返回x值之后,x变量加1
例子: 以etc下的passwd文件为例子
1 正则表达式
eg:
awk 'BEGIN {FS=”:”} $0~/root/' passwd
(在passwd文件中查找 以:为分割符,全域$0与root(这个是正则表达式)匹配的行)
结果:root:x:0:0:root:/root:/bin/bash
eg: awk 'BEGIN {FS=”:”} $0!~/root/' passwd
(在passwd中查找 全域 不与root匹配的行)
2运算符
eg
awk 'BEGIN {FS=”:”} {if($3==7||$4==7) print $0}' passwd
将第三域 或者 第四域 等于7 的记录打印出来
结果:
awk: 1: unexpected character 0xe2
eg : awk 'BEGIN {FS=":"} {if($3~7||$4~7) print $0}' passwd
将第三域 或者 第四域 包含7 的记录打印出来
结果:
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
avahi-autoipd:x:106:117:Avahi autoip daemon,,,:/var/lib/avahi-autoipd:/bin/false
avahi:x:107:118:Avahi mDNS daemon,,,:/var/run/avahi-daemon:/bin/false
hplip:x:113:7:HPLIP system user,,,:/var/run/hplip:/bin/false
sshd:x:117:65534::/var/run/sshd:/usr/sbin/nologin
=与~区别:!!
= 表示 某个域与 7 当作值全域比较!! 相等!!!
而~表示 的是 正则表达式!!!! 匹配的是字符串!!!
对于不匹配:应该这样写:
awk 'BEGIN {FS=":"} { if( $1!~/anders/) print $0}'
注意: 是 !~ 这两个连在一起的!!!
eg:
awk '/^$/{print x+=1}' file
等价于: awk '{$0~/^$/} {print x+=1}' file
awk '{/^$/} {print x+=1}' file
找出file1中的空白行,找到一个空白行 x就加1
eg:
file4的内同:
zhangsan class7 2009,91,92,90
lisi class6 2009,88,87,95
wangwu class1 2008,91,98,85
脚本:file3,awk的内容:
#!/usr/bin/awk -f
BEGIN {FS=","}
{total=$2+$3+$4
avg=total/5
print $1,avg}
注意一点: 这个脚本里德 total,avg变量在使用的时候不加$ !!
(计算每个学生的平均成绩)
执行:./file3.awk file
结果:
zhangsan class7 2009 54.6
lisi class6 2009 54
wangwu class1 2008 54.8
4 awk系统变量:
awk定义了很多内建变量用于设置环境信息,我们称它们为系统变量,这些系统变量可分为两种:
第1种:用于改变awk的缺省值,如域分隔符;
第2种:用于定义系统值,在处理文本时可以读取这些系统值,如记录中的域数量、当前记录数、当前文件名等,awk动态改变第2种系统变量的值
变量名 意义
$n 当前记录的第n个域,域间由FS分割
$0 记录的所有域
ARGC 命令行参数的数量
ARGIND 命令行中当前文件的位置(以0开始标号)
ARGV 命令行参数的数组
CONVFMT 数字转换格式
ENVIRON 环境变量关联数组
ERRNO 最后一个系统错误的描述
FIELDWIDTHS 字段宽度列表,以空格键分割
FILENAME 当前文件名
FNR 浏览文件的记录数 file number row
FS 字段分隔符,默认是空格键 Field Sign
IGNORECASE 布尔变量,如果为真,则进行忽略大小写的匹配
NF 当前记录中的域数量
NR 当前记录数
OFMT 数字的输出格式
OFS 输出域分隔符,默认是空格键
ORS 输出记录分隔符,默认是换行符
RLENGTH 由match函数所匹配的字符串长度
RS 记录分隔符,默认是空格键
RSTART 由match函数所匹配的字符串的第1个位置
SUBSEP 数组下标分隔符,默认值是\034
eg:
awk 'BEGIN {FS=”:”} {print NF,NR,$0} END {print FILENAME}' file4
结果:
4 1 zhangsan class7 2009,91,92,90
4 2 lisi class6 2009,88,87,95
4 3 wangwu class1 2008,91,98,85
0 4
5 awk格式化输出:
6 awk 内置的字符串函数awk的一大主要功能是产生报表,报表就要求按照预定的格式输出,awk借鉴C语言的语法,定义了printf输出语句,它可以规定输出的格式。
printf:printf (格式控制符,参数)
格式控制符:awk修饰符,格式符
修饰符 意义
- 左对齐
width 域的步长
.prec 小数点右边的位数
运算符 意义
%c ASCII字符
%d 整型数
%e 浮点数,科学记数法
%f 浮点数
%o 八进制数
%s 字符串
%x 十六进制数
练习:
文件内容:
zhang san,21,beijing,010
li si,23,wuhan,027
wang wu,22,chengdu,024
eg:
awk 'BEGIN {FS=”,”} {printf(“%s\t%d\n”,$1,$2)}' file3
(将第一域为字符串 第二域为数字 打印出来)
结果:
zhang san 21
li si 23
wang wu 22
eg:
awk 'BEGIN {FS=”,”} {printf(“%-10s%s\n”,$1,$3)}' file3
(红色表示 该所占位置为10个字符!)
结果:
zhang san beijing
li si wuhan
wang wu chengdu
由结果可以看出:beijing wuhan chengdu 之前都是10个字符 所以他们的会并齐显示!!
eg
awk 'BEGIN {FS=",";print "NAME\t\t\tCITY"} {printf("%-10s\t\t%s\n",$1,$3)}' file3
(第一个{}中的两个命令中间是;分号 后面 红色的命令是打印出 列名)
结果;
NAME CITY
zhang san beijing
li si wuhan
wang wu chengdu
7 awk的条件语句 和循环语句awk内置字符串函数极为强大,是Shell处理字符串的常用工具
函数名 意义
gsub(r,s)在输入文件中用s替换r
gsub(r,s,t)在t中用s替换r
index(s,t)返回s中字符串第一个t的位置
length(s)返回s的长度
match(s,t)测试s是否包含匹配t的字符串
split(r,s,t)在t上将r分成序列s
sub(r,s,t)将t中第1次出现的r替换为s
substr(r,s)返回字符串r中从s开始的后缀部分
substr(r,s,t)返回字符串r中从s开始长度为t的后缀部分
Eg
awk 'BEGIN {print index(“this is a dog”,”ls”)}'
结果:3
eg
awk 'BEGIN {print length(“this is a dog”)}'
结果:13
eg
awk 'BEGIN {print match(“this is a dog”,/is/)}'
结果:3
eg
awk 'BEGIN {str="this is a dog";printsubstr(str,3)}'
结果:is is a dog
eg
awk 'BEGIN {str=”this is a dog;printsubstr(str,3,4)”}'
结果: is i
在linux awk的 while、do-while和for语句中允许使用break,continue语句来控制流程走向,也允许使用exit这样的语句来退出。break中断当前正在执行的循环并跳到循环外执行下一条语句。if 是流程选择用法。 awk中,流程控制语句,语法结构,与c语言类型。下面是各个语句用法。
一.条件判断语句(if)
if(表达式) #if ( Variable in Array )
语句1
else
语句2
格式中"语句1"可以是多个语句,如果你为了方便Unix awk判断也方便你自已阅读,你最好将多个语句用{}括起来。Unix awk分枝结构允许嵌套,其格式为:
if(表达式)
{语句1}
else if(表达式)
{语句2}
else
{语句3}
eg:
anders@AndersPC:~/code/shell/awk$ awk 'BEGIN {
grade=97;
f(grade>85){
print "good";
}else if(grade>70){
print "ok";
}else{
print "bad";
}}'
good
每条命令语句后面可以用“;”号结尾。
二.循环语句(while,for,do)
1.while语句
格式:
while(表达式)
{语句}
eganders@AndersPC:~/code/shell/awk$ awk 'BEGIN {
total=0;
while(i<=100){
total+=i;i++;
}
print total;
}'
5050
2.for 循环
for循环有两种格式:
格式1:
for(变量 in 数组)
{语句}
eg
anders@AndersPC:~/code/shell/awk$ awk 'BEGIN {for(i in ENVIRON){
print i"="EBVIRON[i];
}
}'
UBUNTU_MENUPROXY=libappmenu.so
SSH_AUTH_SOCK=/tmp/keyring-u2bskH/ssh
QT_IM_MODULE=xim
DEFAULTS_PATH=/usr/share/gconf/ubuntu.default.path
GNOME_KEYRING_CONTROL=/tmp/keyring-u2bskH
DESKTOP_SESSION=ubuntu
HOME=/home/anders。。。。。。
说明:ENVIRON 是awk常量,是子典型数组。上面有介绍这个常量的
格式2:
for(变量;条件;表达式)
{语句}
例子:
anders@AndersPC:~/code/shell/awk$ awk 'BEGIN {total=0;
for(i=1;i<=100;i++){
total+=i;
}
print total;
}'
5050
其实很多shell程序都可以交给awk,而且性能是非常快的。
break 当 break 语句用于 while 或 for 语句时,导致退出程序循环。
continue 当 continue 语句用于 while 或 for 语句时,使程序循环移动到下一个迭代。
next 能能够导致读入下一个输入行,并返回到脚本的顶部。这可以避免对当前输入行执行其他的操作过程。
exit 语句使主输入循环退出并将控制转移到END,如果END存在的话。如果没有定义END规则,或在END中应用exit语句,则终止脚本的执行。测试一下性能:
anders@AndersPC:~/code/shell/awk$ time (total=0;for i in $(seq 10000);do let total+=$i;done;echo $total;)
50005000
real 0m0.163s
user 0m0.144s
sys 0m0.012sanders@AndersPC:~/code/shell/awk$ time awk 'BEGIN {total=0;for(i=1;i<=10000;i++){total+=i;}print total;}'
50005000
real 0m0.003s
user 0m0.000s
sys 0m0.000s
差了很多!!!!相当的多啊 !!有没有!
8 AWK数组
基本概念:
数组是用于存储一系列值的变量,这些值之间通常是有联系的,可通过索引来访问数组的值,索引需要用中括号括起,数组的基本格式为:array[index]=value
关联数组是指数组的索引可以是字符串,也可以是数字!!!!使用数字下标时,称为索引数组;使用字符串下标时,称为关联数组。
关联数组在索引和数组元素值之间建立起关联,对每一个数组元素,awk自动维护了一对值:索引和数组元素值
关联数组的值无需以连续的地址进行存储!!!!!,awk的所有数组都是关联数组,下标可以是数字和字符串。因无需对数组名和元素提前声明,也无需指定元素个数 ,所以awk的数组使用非常灵活。下标可以是数字和字符串。因无需对数组名和元素提前声明,也无需指定元素个数 ,所以awk的数组使用非常灵活。
字符串和数字之间的差别是明显的,如,我们使用array[09]指定一个数组值,如果换成array[9]就不能指定到与array[09]相同的值!!!!!!!!!!
数组的赋值和引用
语法格式如下:
array[index] = value
1 index 是数组的下标,下标可以是数字、字符串、变量、表达式;value 是数组元素的值。
2 awk 只提供了这种数组赋值方式(单个元素赋值)。
3 引用数组元素的时候,需要使用 array[index] 形式。
4 引用不存在的数组下标,将创建该数组元素。数组长度将变大。
eg
arr1.sh
#!/usr/bin/awk -f
BEGIN {
str["name"]="Amders";
old[1]=22;
print str["name"] " " old[1];
}anders@AndersPC:~/code/shell/awk$ ./arr1.sh
Amders 22
eg
split(r,s,t)函数将字符串以t为分隔符,将r字符串拆分为字符串数组,并存放在s中,此时s通常就是一个数组
awk 'BEGIN {print split("abc/def/xyz",str,"/")}'
上面命令以“/”为分隔符,将字符串abc/def/xyz分开,并存在str数组中,split函数的返回值是数组的大小
awk可使用for循环打印数组内容
for (variable in array)
do something with array[variable]
eg
awk 'BEGIN {print split("abc/def/xyz",str,"/")}'`
结果: 3
数组遍历:
使用 for 循环(for (expression1; expression2; expression3))遍历数组。
#!/usr/bin/awk -f
BEGIN {
str[1]="a";
str[2]="b";
str[3]="c";
for(i=1;i<=3;i++)
print str[i];
}
ARGV 命令行参数的数组(上面已经介绍过)
ARGC是ARGV数组中元素的个数,与C语言一样,从ARGV[0]开始,到ARGV[ARGC-1]结束
说明了ARGV数组中存储了什么
ARGV[0]中存储的是awk,即执行该脚本的程序名
ARGV[1]到ARGV[ARGC-1]存储了脚本后跟的位置参数
eg:
argv.awk 脚本的内容:
BEGIN {for(i=0;i<ARGC;i++)
print ARGV[i]
print ARGC
}
执行脚本:
awk -f ./argv.awk abc a=1 “hello world”
(因为在argv.awk脚本文件中 并没有添加#!/usr/bin/awk -f,所以要在执行命令的时候加上)
结果:
awk
abc
a=1
hello world
4
结果表明: ARGV[0]中存储的是awk,即执行该脚本的程序名