Shell 学习7(awk命令)

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;"
}

执行: 

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;


2 awk修改分割符 两种方式: -F  , FS变量


(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格式化输出:

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


6 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


 


7 awk的条件语句 和循环语句

在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(表达式)
{语句}


eg

anders@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.012s

anders@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,即执行该脚本的程序名


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值