Fung's DBA World

DBA knowledge,standing on the shoulders of giants.

[Shell学习笔记]awk基础1

September 27, 2013

awk是一种用于处理数据和生成报告的UNIX编程语言。nawk是awk的新版本,gawk是基于Linux的GNU版本。 awk 以逐行方式扫描文件,从第一行到最后一行,以查找匹配某个特定模式的问本行,并对这些文本执行(括号在花括号中的)指定动作。如果只给出模式而未指定动作,则所有该匹配的模式的行都显示在屏幕上;如果只指定动作而未定义模式,会对所有输入行指定指定动作。

1.awk格式

以下面这个文本为例,简单说明awk的用法。
1
2
3
4
5
6
7
[root@linora shell]# cat employee 
Tom Jones       4424    5/12/66 54335 
Mary Adams      5346    11/4/63 28765 
Sally Chang     1654    7/22/54 65000 
Billy Black     1683    9/23/44 33650 
[root@linora shell]# awk '/Tom/' employee 
Tom Jones       4424    5/12/66 54335 
说明: 打印含有模式Tom的行。
1
2
3
4
5
[root@linora shell]# awk '{print $1}' employee 
Tom 
Mary 
Sally 
Billy 
说明: 打印文件第一个字段,字段从行的左端开始,以空白符分隔。如果需要自定义分隔符,则需加上-F的参数。
1
2
[root@linora shell]# awk '/Tom/{print $1,$2}' employee 
Tom Jones 
说明: 打印含有模式Tom的第一个和第二个字段。注意,字段中间有逗号分隔,它在awk中映射为一个内部变量,称为输出字段分隔符(output field separator),OFS默认为空格,逗号被OFS变量中存储的字符替换。
1
2
3
[root@linora shell]# df |awk '$4>10240000' 
Filesystem           1K-blocks      Used Available Use% Mounted on 
/dev/sda3             28337624  14901832  11973076  56% / 
说明: df为文件系统磁盘剩余空间报告。df命令的输出通过pipe发给awk,如果其中某行的第四个字段大于10G,则该行被打印。
1
2
[root@linora shell]# w |awk '/^oracle/{print $3}' 
192.168.56.1 
说明: w命令是Linux下查看有哪些用户登录做了哪些事情的命令。通过pipe将w的输出命令传给awk,如果w结果含有以oracle开头的行,则打印其第三个字段,即查找以oracle用户登录的IP地址。

2.格式化输出

2.1.print函数

awk命令的操作部分被括在大括号重化工。print函数用于打印不需要特别编排格式简单输出。更为复杂的格式编排则要使用printf或sprintf函数。

在awk命令中,可以用{print}形式显式地调用print函数。其参数可以是变量、数值或字符串常量。字符串必须用双引号括起来。参数之间用逗号分隔,逗号等价于OFS中的值,默认情况下是空格。如果没有逗号分隔,那么所有参数将会被串在一起。

1
2
3
4
5
[root@linora shell]# date 
Thu Sep 26 23:41:52 CST 2013 
[root@linora shell]# date |awk '{print "Month:",$2 "\nYear:",$6}' 
Month: Sep 
Year: 2013 
上例中的“\n”为转义字符,见下表。

表2-1     print函数使用的转义序列

转义序列 含义
\b 退格
\f 换页
\n 换行
\r 回车
\t 制表符,即tab键
\047 八进制值47,即单引号
\c c代表任一其他字符,如“\”
1
2
[root@linora shell]# awk '/Tom/{print "\t\tHave a nice day," $1,$2 "!!!"}' employee 
Have a nice day,Tom Jones!!! 

2.2.printf函数

打印输出时,可能需要指定字段间的空格数,从而把列排整齐。在print函数中使用制表符并不能保证得到想要的输出,因此可用printf来格式化特别输出。

printf函数返回一个带格式的字符串给标准输出,其语句包括一个加引号的控制串,控制串中可能嵌有若干格式说明和修饰符。控制串后面跟一个逗号,之后是一列由逗号分隔的表达式。printf函数根据控制串中的说明编排这些表达式的格式,但printf不会在行尾自动换行,需要在控制串中提供转义字符\n。

表2-2     printf使用的转义字符

转义字符 定义
c 字符
s 字符串
d 十进制整数
ld 十进制长整数
u 十进制无符号整数
lu 十进制无符号长整数
x 十六进制整数
lx 十六进制长整数
o 八进制整数
lo 八进制长整数
e 用科学计数法表示的浮点数
f 浮点数
g 选用e或者f中较短的一种形式

表2-3     printf的修饰符

字符 定义
- 左对齐修饰符
# 显示8进制整数时在前面加个0显示16进制整数时在前面加0x
+ 显示使用d、e、f、和g转换的整数时,加上正负号
0 用0而不是空白符来填充所显示的值

表2-4     printf的格式说明符

格式说明符 功能
假定x=’A’、y=15、z=2.3且$1=Bob Smith
%c printf("The character is %c\n",x)output:The character is A
%d printf("The boy is %d years old!\n",y)output: The boy is 15 years old!
%e [root@linora shell]# echo "2.3" |awk '{printf("z is %e\n",$1)}'z is 2.300000e+00
%f [root@linora shell]# echo "2.3" |awk '{printf("z is %f\n",$1)}'z is 2.300000
%o [root@linora shell]# echo "15" |awk '{printf("z is %o\n",$1)}'z is 17
%s printf("His name is %s\n",$1)打印字符串:His name is Bob smith
%x [root@linora shell]# echo "15" |awk '{printf("y is %x\n",$1)}'y is f
下面这个例子中,printf控制串里的pipe字符是文本的一部分,用于指示格式的开始与结束。
1
2
3
4
[root@linora shell]# echo "UNIX"|awk '{printf "|%-15s|\n",$1}' 
|UNIX           | 
[root@linora shell]# echo "UNIX"|awk '{printf "|%15s|\n",$1}' 
|           UNIX| 
通过以上输出控制,可以控制文本靠左靠右对齐,百分号让printf做好准备,它要打印一个占15格,向左对其的字符串,这个字符串夹在两个pipe符号间,并且以换行符结尾。百分号后的减号表示左对齐。无减号表示右对齐。
1
2
3
4
5
[root@linora shell]# awk '{printf "The name is:%-15s ID is %8d\n",$1,$3}' employee 
The name is:Tom             ID is     4424 
The name is:Mary            ID is     5346 
The name is:Sally           ID is     1654 
The name is:Billy           ID is     1683 
上例中,要打印的字符放置在两个引号之间。第一个格式说明符是%-15s,它对应的参数是$1。格式%8d表示在字符串的这个位置打印$3的十进制值。这个整数占8格,向右对齐。也可以将加引号的字符串和表达式放在圆括号内。
1
awk '{printf ("The name is:%-15s ID is %8d\n",$1,$3)}' employee 

2.3.记录与字段

awk在默认情况下,每一行称为一条记录,以换行符结束,输入和输出的分隔符都是换行符,分别保存在awk的内置变量ORS和RS中。同时awk用$0代表整条记录,变量NR保存每条记录的记录号,每处理完一条记录NR自动加1.
1
2
3
4
5
[root@linora shell]# awk '{print NR,$0}' employee 
1 Tom Jones     4424    5/12/66 54335 
2 Mary Adams    5346    11/4/63 28765 
3 Sally Chang   1654    7/22/54 65000 
4 Billy Black   1683    9/23/44 33650 
每条记录都是由称为字段的词组成,字段由美元符号和字段编号做标记。默认情况下,字段间用空白符(空格或者制表符)分隔。每条记录的字段值保存在内置变量NF中,NF的值因行而异。
1
2
3
4
5
[root@linora shell]# awk '{print NR,$1,$2,$3,NF}' employee 
1 Tom Jones 4424 5 
2 Mary Adams 5346 5 
3 Sally Chang 1654 5 
4 Billy Black 1683 5 
awk的内置变量FS中保存了输入字段分隔符的值。默认用空格或者制表符,并且删除各字段前多于的空格或制表符。同时,可以使用-F参数修改FS值。
1
2
3
4
[root@linora shell]# awk '/oracle/{print $1}' /etc/passwd 
oracle:x:500:500::/home/oracle:/bin/bash 
[root@linora shell]# awk -F: '/oracle/{print $1,$2,$3}' /etc/passwd 
oracle x 500 
上例中,使用-F参数,后面紧跟冒号来取代默认的输入分隔符。 我们也可以指定多个输入字段分隔符,如果有多个字符被用于字段分隔符FS,则FS对应是一个正则表达式字符串,并且被括在方括号内。下面的例子中(注意冒号前有空格),FS的值是空格,冒号或者制表符。
1
2
3
4
[root@linora shell]# sed -n '/Tom/p' employee2 
Tom Jones:4424:5/12/66:54335 
[root@linora shell]# awk -F'[ :\t]' '/Tom/{print $1,$2,$3}' employee2 
Tom Jones 4424 
输出分隔符被保存在内置变量OFS中,默认都是单个空格。如果需要修改OFS值,可以参照以下例子。但无论OFS如何设置,print语句中用于分隔字段的逗号,在输出时候都被转换成OFS的值。
1
2
[root@linora shell]# awk -F: 'BEGIN {OFS=":"}/oracle/{print $1,$2,$3}' /etc/passwd 
oracle:x:500 

3.awk内置变量

表2-5     awk内置变量

变量 描述 变量 描述
$n 当前记录的第n个字段,字段间由FS分隔 FIELDWIDTHS 字段宽度列表
$0 完整的输入记录 ARGC 命令行参数的数目
ARGIND 命令行当前的文件的位置 ARGV 包含命令行参数的数组
CONVFMT 数字转换格式,默认为%.6g ENVIRON 环境变量关联数组
ERRNO 最后一个系统错误的描述 FILENAME 当前文件名
FNR 同NR,但相对于当前文件 FS 输入字段分隔符
NF 当前记录的字段数 IGNORECASE 如果为真,则忽略大小写
NR 当前记录数 OFMT 数字的输出格式,默认%.6g
OFS 输出字段分隔符 ORS 输出记录分隔符
RLENGTH 由match函数所匹配的字符串长度 RSTART 由match函数所匹配的字符串的第一个位置
RS 输入记录分隔符 SUBSEP 数组下标分隔符

4.模式与操作

awk模式用来控制对输入的文本行执行什么操作。模式由正则表达式、判别条件真伪的表达式或者两者的组合构成。awk默认操作是打印所有表达式为真的文本行。如果模式表达式含有if的意思,就不必用花括号把它括起来。
1
2
[root@linora shell]# awk '/Tom/' employee 
Tom Jones       4424    5/12/66 54335 
操作(action)是花括号中以分号分隔的语句。如果操作前面有个模式,则该模式控制执行操作的时间。同一行的多条语句由分号分隔,独占一行的语句则以换行符分隔。

5.正则表达式

对awk而言,正则表达式是置于两个斜杠之间、由字符组组成的模式。Awk支持使用(与egrep相同的)正则表达式元字符对正则表达式进行某种方式的修改。如果输入行中的某个字符串与正则表达式相匹配,则最终条件为真,于是执行与该表达式关联的所有操作,如果没有指定操作,则打印与正则表达式匹配的记录。

表2-6     awk的正则表达式元字符

元字符 说明
^ 在串首匹配
$ 在串尾匹配
. 匹配任意单个字符
* 匹配零个或者多个前导字符
+ 匹配一个或者多个前导字符
? 匹配零个或者一个前导字符
[ABC] 匹配指定字符组中任意一个字符
[^ABC] 匹配任何一个不在指定字符组内的字符
[A-Z] 匹配A-Z之间的任一字符
A|B 匹配A或者B
(AB)+ 匹配一个或者多个AB组合,即AB、ABAB等
\* 匹配星号本身
& 用在替代串中,代表查找传中匹配到的内容
表2-7列出的元字符,大多数版本的grep和sed都支持,但是任何版本的awk都不支持。对于POSIX支持的括号类正则表达式,除了gawk支持外,awk及nawk均不支持。

表2-7     awk不支持的元字符

元字符 说明
\<\> 单词定位
\(\) 向前引用
\{\} 重复
匹配操作符(~)用于对记录或字段的表达式进行匹配。下例中,表示显示所有在第一个字段里匹配到Tom或者tom的行。
1
2
[root@linora shell]# awk '$1 ~ /[tT]om/' employee 
Tom Jones       4424    5/12/66 54335 
下面这个例子中,显示所有不是以ly结尾的行。
1
2
3
[root@linora shell]# awk '$1 !~ /ly$/' employee 
Tom Jones       4424    5/12/66 54335 
Mary Adams      5346    11/4/63 28765 

6.awk脚本范例

在awk脚本中,如果同一行中有多条语句或操作,必须用分号将它们分隔开。如果每条语句都在不同的行上,就不需要分号分开。如果操作跟在某个模式后面,它的左花括号就必须与该模式位于同一行,注释以#开头。
1
2
3
4
5
6
7
8
9
[root@linora shell]# cat awkfile 
#My first awk script 
/Tom/{print "Tom's birthday is "$3} 
/Mary/{print NR, $0} 
/^Sally/{print "Hi,Sally. " $1 " has a salary of $" $4 "."} 
[root@linora shell]# awk -F: -f awkfile employee2 
Tom's birthday is 5/12/66 
2 Mary Adams:5346:11/4/63:28765 
Hi,Sally. Sally Chang has a salary of $65000. 
第一行以#开头,表示是注释。 第二行表示查找模式含有Tom的行,并且通过print函数打印”Tom’s birthday is”和该行的第三个字段的值。 第三行,查找含有模式Mary的行,打印整行,并且加上记录号。 第四行,查找以Sally开头的行,依次打印”Hi Sally.”、该行第一个字段值、字符串”has a salary of $”和该行第四个字段的值。
Write an awk script called facts that
a.Prints full names and phone numbers for the Savages.
b.Prints Chet's contributions.
c.Prints all those who contributed $250 the first month.
答案(为了方便,作了个简单排序):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@linora shell]# cat facts 
$2 ~ /Savage/{print "1st request:  " $1,$2,$3,$4} 
/Chet/{print "2nd request:  " $5,$6,$7} 
$5 ~ /^250$/{print "3rd request:  " $1,$2} 
[root@linora shell]# awk -F"[ :]" -f facts lab3.data |sort -n 
1st request:  Dan Savage (406) 298-7744 
1st request:  Jody Savage (206) 548-1278 
1st request:  Tom Savage (408) 926-3456 
2nd request:  50 95 135 
3rd request:  Archie McNichol 
3rd request:  Guy Quigley 
3rd request:  John Goldenrod 
3rd request:  Mike Harrington 
3rd request:  Nancy McNeil 
3rd request:  Susan Dalsass 
3rd request:  Tom Savage 
EOF

Permalink: http://www.oraclema.com/linux/awk_fundamental1.html