Awk详解

1. awk基本原理

通过一个简短的命令,我们来了解其工作原理。

awk '{print $0}' /etc/passwd
echo hhh|awk '{print "hello,world"}'
awk '{ print "hiya" }' /etc/passwd

你将会见到/etc/passwd 文件的内容出现在眼前。现在,解释 awk 做了些什么。调用 awk时,我们指定/etc/passwd 作为输入文件。执行 awk 时,它依次对/etc/passwd 中的每一行执行 print 命令。所有输出都发送到 stdout,所得到的结果与执行 cat /etc/passwd 完全相同。

现在,解释{ print }代码块。在 awk 中,花括号用于将几块代码组合到一起,这一点类似于 C 语言。在代码块中只有一条 print 命令。在 awk 中,如果只出现 print 命令,那么将打印当前行的全部内容。

再次说明,awk 对输入文件中的每一行都执行这个脚本。

$ awk -F":" '{ print $1 }' /etc/passwd
$ awk -F":" '{ print $1 $3 }' /etc/passwd
$ awk -F":" '{ print $1 " " $3 }' /etc/passwd
$ awk -F":" '{ print "username: " $1 "\t\tuid:" $3" }' /etc/passwd

 

1.1 BEGIN 和 END 模块 

通常,对于每个输入行,awk 都会执行每个脚本代码块一次。然而,在许多编程情况中,可能需要在 awk 开始处理输入文件中的文本之前执行初始化代码。对于这种情况,awk 允许您定义一个 BEGIN 块。我们在前一个示例中使用了 BEGIN 块。因为 awk 在开始处理输入文件之前会执行 BEGIN 块,因此它是初始化 FS(字段分隔符)变量、打印页眉或初始化其它在程序中以后会引用的全局变量的极佳位置。

awk 还提供了另一个特殊块,叫作 END 块。awk 在处理了输入文件中的所有行之后执行
这个块。通常,END 块用于执行最终计算或打印应该出现在输出流结尾的摘要信息。

1.2 运算符

运算符                                     描 述

赋值运算符
= += -= *= /= %= ^= **=           赋值语句

逻辑运算符
||                                 逻辑或
&&                              逻辑与

正则运算符
~ !~                             匹配正则表达式和不匹配正则表达式

关系运算符
< <= > >= != ==          关系运算符

算术运算符
+ -                 加,减
* / &               乘,除与求余
+ - !               一元加,减和逻辑非
^ ***              求幂
++ --             增加或减少,作为前缀或后缀

其他运算符
$                   字段引用
空格              字符串链接符
?:                  三目运算符
In                  数组中是否存在某键值

awk 赋值运算符

a+5;等价于:a=a+5;其他同类

[root@yum tmp]# awk 'BEGIN{a=5;a+=5;print a}'
10

awk 逻辑运算符

[root@yum tmp]# awk 'BEGIN{a=1;b=2;print (a>2&&b>1,a=1||b>1)}'
0 1

awk 正则运算符

[root@yum tmp]# awk 'BEGIN{a="100testaaa";if(a~/100/){print "ok"}}'
ok
[root@yum tmp]# echo|awk 'BEGIN{a="100testaaa"}a~/100/{print "ok"}'
ok

关系运算符

如:> < 可以作为字符串比较,也可以用作数值比较,关键看操作数如果是字符串就会转换为字符串比较。两个都为数字才转为数值比较。字符串比较:按照ascii码顺序比较 。

[root@yum tmp]# awk 'BEGIN{a="11";if(a>=9){print "ok"}}'
[root@yum tmp]# awk 'BEGIN{a=11;if(a>=9){print "ok"}}'
ok
[root@yum tmp]# awk 'BEGIN{a;if(a>=b){print "ok"}}'
ok

awk 算术运算符

说明,所有用作算术运算符进行操作,操作数自动转为数值,所有非数值都变为0。

[root@yum tmp]# awk 'BEGIN{a="b";print a++,++a}'
0 2
[root@yum tmp]# awk 'BEGIN{a="20b4";print a++,++a}'
20 22

其他运算符

?:三目运算符

[root@yum tmp]# awk 'BEGIN{a="b";print a=="b"?"ok":"err"}'
ok
[root@yum tmp]# awk 'BEGIN{a="b";print a=="c"?"ok":"err"}'
err

1.3 常用 awk 内置变量  

变量名         属 性
$0          当前记录
$1~$n    当前记录的第 n 个字段
FS         输入字段分隔符 默认是空格
RS        输入记录分割符 默认为换行符
NF        当前记录中的字段个数,就是有多少列
NR        已经读出的记录数,就是行号,从 1 开始
OFS      输出字段分隔符 默认也是空格
ORS      输出的记录分隔符 默认为换行符

字段分隔符 FS

FS="\t" 一个或多个 Tab 分隔

[root@yum tmp]# awk 'BEGIN{FS="\t+"}{print $1,$2,$3}' tab.txt
ww CC IDD
[root@yum tmp]# cat tab.txt
ww CC IDD
[root@yum tmp]# awk 'BEGIN{FS="\t+"}{print $1,$2,$3}' tab.txt
ww CC IDD

FS="[[:space:]+]" 一个或多个空白空格,默认的

[root@yum tmp]# cat space.txt
we are studing awk now!
[root@yum tmp]# awk -F [[:space:]+] '{print $1,$2,$3,$4,$5}' space.txt
we are
[root@yum tmp]# awk -F [[:space:]+] '{print $1,$2}' space.txt
we are

FS="[" ":]+" 以一个或多个空格或:分隔

[root@yum tmp]# cat hello.txt
root:x:0:0:root: /root:/bin/bash
[root@yum tmp]# awk -F [" ":]+ '{print $1,$2,$3}' hello.txt
root x 0

字段数量 NF

[root@yum tmp]# cat hello.txt
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin:888
[root@yum tmp]# awk -F ":" 'NF==8{print $0}' hello.txt
bin:x:1:1:bin:/bin:/sbin/nologin:888

记录数量 NR

[root@yum tmp]# ifconfig eth0| awk -F [" ":]+ 'NR==2{print $4}'
192.168.68.33

RS 记录分隔符变量

将 FS 设置成"\n"告诉 awk 每个字段都占据一行。通过将 RS 设置成"",还会告诉 awk每个地址记录都由空白行分隔。

[root@yum tmp]# cat recode.txt
Jimmy the Weasel
100 Pleasant Drive
San Francisco, CA 12345
#此处是空白行
Big Tony
200 Incognito Ave.
Suburbia, WA 67890
[root@yum tmp]# cat awk.txt
#!/bin/awk
BEGIN {
FS="\n"
RS=""
}
{
print $1 ", " $2 ", " $3
}
[root@yum tmp]# awk -f awk.txt recode.txt
Jimmy the Weasel, 100 Pleasant Drive, San Francisco, CA 12345
Big Tony, 200 Incognito Ave., Suburbia, WA 67890

OFS 输出字段分隔符

[root@yum tmp]# cat hello.txt
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin:888
[root@yum tmp]# awk 'BEGIN{FS=":"}{print $1","$2","$3}' hello.txt
root,x,0
bin,x,1
[root@yum tmp]# awk 'BEGIN{FS=":";OFS="#"}{print $1,$2,$3}' hello.txt
root#x#0
bin#x#1

ORS 输出记录分隔符

[root@yum tmp]# cat recode.txt
Jimmy the Weasel
100 Pleasant Drive
San Francisco, CA 12345
Big Tony
200 Incognito Ave.
Suburbia, WA 67890
[root@yum tmp]# cat awk.txt
#!/bin/awk
BEGIN {
FS="\n"
RS=""
ORS="\n\n"
}
{
print $1 ", " $2 ", "$3
}
[root@yum tmp]# awk -f awk.txt recode.txt
Jimmy the Weasel, 100 Pleasant Drive, San Francisco, CA 12345
Big Tony, 200 Incognito Ave., Suburbia, WA 67890

2. awk 正则  

 

规则表达式

awk '/REG/{action} ' file,/REG/为正则表达式,可以将$0 中,满足条件的记录送入到:action 进行处理

[root@yum tmp]# awk '/root/{print $0}' passwd
root:x:0:0:root: /root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
[root@yum tmp]# awk -F : '$5~/root/{print $0}' passwd
root:x:0:0:root: /root:/bin/bash
[root@yum tmp]# ifconfig eth0|awk 'BEGIN{FS="[[:space:]:]+"} NR==2{print $4}' #取出 ip
192.168.68.33

布尔表达式

awk '布尔表达式{action}' file 仅当对前面的布尔表达式求值为真时,awk 才执行代码块。

[root@yum tmp]# awk -F: '$1=="root"{print $0}' passwd
root:x:0:0:root: /root:/bin/bash
[root@yum tmp]# awk -F: '($1=="root")&&($5=="root"){print $0}' passwd
root:x:0:0:root: /root:/bin/bash

3. awk 条件与循环

3.1 条件语句

awk 提供了非常好的类似于 C 语言的 if 语句。

{
if ( $1== "foo" ) {
if ( $2== "foo" ) {
print "uno"
} else {
print "one"
}
} elseif ($1== "bar" ) {
print "two"
} else {
print "three"
}
}

使用 if 语句还可以将代码:

! /matchme/ { print $1 $3 $4 }

转换成:

{
if ( $0 !~ /matchme/ ) {
print $1 $3 $4
}
}

3.2 循环结构

我们已经看到了 awk 的 while 循环结构,它等同于相应的 C 语言 while 循环。awk 还有"do...while"循环,它在代码块结尾处对条件求值,而不像标准 while 循环那样在开始处求值。它类似于其它语言中的"repeat...until"循环。以下是一个示例:

do...while 示例

{
count=1
do {
print "I get printed at least once no matter what"
} while ( count !=1 )
}

与一般的 while 循环不同,由于在代码块之后对条件求值,"do...while"循环永远都至少执行一次。换句话说,当第一次遇到普通 while 循环时,如果条件为假,将永远不执行该循环。

for 循环

awk 允许创建 for 循环,它就象 while 循环,也等同于 C 语言的 for 循环:

for ( initial assignment; comparison; increment ) {
code block
}

以下是一个简短示例:

for ( x=1;x<=4;x++ ) {
print "iteration", x
}

break 和 continue

此外,如同 C 语言一样,awk 提供了 break 和 continue 语句。使用这些语句可以更好地控制 awk 的循环结构。以下是迫切需要 break 语句的代码片断:

while 死循环
while (1) {
print "forever and ever..."
}
while 死循环 1 永远代表是真,这个 while 循环将永远运行下去。

以下是一个只执行十次的循环:

#break 语句示例
x=1
while(1) {
print "iteration", x
if ( x==10 ) {
break
}
x++
}

这里,break 语句用于“逃出”最深层的循环。"break"使循环立即终止,并继续执行循
环代码块后面的语句。
continue 语句补充了 break,其作用如下:

x=1
while (1) {
if ( x==4 ) {
x++
continue
}
print "iteration", x
if ( x>20 ) {
break
}
x++
}

这段代码打印"iteration1"到"iteration21","iteration4"除外。如果迭代等于 4,则增加 x并调用 continue 语句,该语句立即使 awk 开始执行下一个循环迭代,而不执行代码块的其余部分。如同 break 一样,continue 语句适合各种 awk 迭代循环。在 for 循环主体中使用时,continue 将使循环控制变量自动增加。以下是一个等价循环:

for ( x=1;x<=21;x++ ) {
if ( x==4 ) {
continue
}
print "iteration", x
}

在 while 循环中时,在调用 continue 之前没有必要增加 x,因为 for 循环会自动增加 x。

4. 数组

AWK 中的数组都是关联数组,数字索引也会转变为字符串索引

{
cities[1]=”beijing”
cities[2]=”shanghai”
cities[“three”]=”guangzhou”
for( c in cities) {
print cities[c]
}
print cities[1]
print cities[“1”]
print cities[“three”]
}

for…in 输出,因为数组是关联数组,默认是无序的。所以通过 for…in 得到是无序的数组。如果需要得到有序数组,需要通过下标获得。

用 awk 中查看服务器连接状态并汇总

netstat -an|awk '/^tcp/{++s[$NF]}END{for(a in s)print a,s[a]}'
ESTABLISHED 1
LISTEN 20

统计 web 日志访问流量,要求输出访问次数,请求页面或图片,每个请求的总大小,总访问流量的大小汇总

awk '{a[$7]+=$10;++b[$7];total+=$10}END{for(x in a)print b[x],x,a[x]|"sort -rn -k1";print
"total size is :"total}' /app/log/access_log
total size is :172230
21 /icons/poweredby.png 83076
14 / 70546
8 /icons/apache_pb.gif 18608

a[$7]+=$10 表示以第 7 列为下标的数组($10 列为$7 列的大小),把他们大小累加得到
$7 每次访问的大小,后面的 for 循环有个取巧的地方,a 和 b 数组的下标相同,所以一
条 for 语句足矣

5. awk 常用函数表

函数说明
gsub( Ere, Repl, [ In ] ) 除了正则表达式所有具体值被替代这点,它和 sub 函数完全一样地执行
sub( Ere, Repl, [ In ] )用 Repl 参数指定的字符串替换 In 参数指定的字符串中的由 Ere参数指定的扩展正则表达式的第一个具体值。sub 函数返回替换的数量。出现在 Repl 参数指定的字符串中的 &(和符号)由 In 参数指定的与 Ere 参数的指定的扩展正则表达式匹配的字符串替换。如果未指定 In 参数,缺省值是整个记录($0 记录变量)
index( String1, String2 ) 在由 String1 参数指定的字符串(其中有出现 String2 指定的参数)中,返回位置,从 1 开始编号。如果 String2 参数不在 String1 参数中出现,则返回 0(零)
length [(String)] 返回 String 参数指定的字符串的长度(字符形式)。如果未给出String 参数,则返回整个记录的长度($0 记录变量)
blength [(String)] 返回 String 参数指定的字符串的长度(以字节为单位)。如果未给出String 参数,则返回整个记录的长度($0 记录变量)
substr( String, M, [ N ] )返回具有 N 参数指定的字符数量子串。子串从 String 参数指定的字符串取得,其字符以 M 参数指定的位置开始。M 参数指定为将String 参数中的第一个字符作为编号 1。如果未指定 N 参数,则子串的长度将是 M 参数指定的位置到 String 参数的末尾 的长度
match( String, Ere )  在 String 参数指定的字符串(Ere 参数指定的扩展正则表达式出现在其中)中返回位置(字符形式),从 1 开始编号,或如果 Ere 参数不出现,则返回 0(零)。RSTART 特殊变量设置为返回值。RLENGTH特殊变量设置为匹配的字符串的长度,或如果未找到任何匹配,则设置为 -1(负一)
split( String, A, [Ere] )将 String 参数指定的参数分割为数组元素 A[1], A[2], . . ., A[n],并返回 n 变量的值。此分隔可以通过 Ere 参数指定的扩展正则表达式进行,或用当前字段分隔符(FS 特殊变量)来进行(如果没有给出 Ere参数)。除非上下文指明特定的元素还应具有一个数字值,否则 A 数组中的元素用字符串值来创建
tolower( String )  返回 String 参数指定的字符串,字符串中每个大写字符将更改为小写。大写和小写的映射由当前语言环境的 LC_CTYPE 范畴定义
toupper( String )  返回 String 参数指定的字符串,字符串中每个小写字符将更改为大写。大写和小写的映射由当前语言环境的 LC_CTYPE 范畴定义
sprintf(Format, Expr,Expr, . . . )根据 Format 参数指定的 printf 子例程格式字符串来格式化 Expr参数指定的表达式并返回最后生成的字符串

5.1 字符串函数的应用

替换

awk 'BEGIN{info="this is a test2010test!";gsub(/[0-9]+/,"!",info);print info}' this is a test!test!

在 info 中查找满足正则表达式,/[0-9]+/ 用”!”替换,并且替换后的值,赋值给 info 未给 info 值,默认是$0

查找

awk 'BEGIN{info="this is a test2010test!";print index(info,"test")?"ok":"no found";}'
ok #未找到,返回 0

匹配查找

awk 'BEGIN{info="this is a test2010test!";print match(info,/[0-9]+/)?"ok":"no found";}'
ok #如果查找到数字则匹配成功返回 ok,否则失败,返回未找到

截取

awk 'BEGIN{info="this is a test2010test!";print substr(info,4,10);}'
s is a tes #从第 4 个 字符开始,截取 10 个长度字符串

分割

awk 'BEGIN{info="this is a test";split(info,tA," ");print length(tA);for(k in tA){print k,tA[k];}}' 4
4 test 1 this 2 is 3 a

#分割 info,动态创建数组 tA,awk for …in 循环,是一个无序的循环。 并不是从数组下标1…n 开始

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值