目录
51.Awk 命令语法
52.Awk 程序结构(BEGIN,body,END)区域
53.打印命令
54.模式匹配
Awk 是一个维护和处理文本数据文件的强大语言。在文本数据有一定的格式,即每行数据包
含多个以分界符分隔的字段时,显得尤其有用。即便是输入文件没有一定的格式,你仍然可
以使用 awk 进行基本的处理。 Awk 当然也可以没有输入文件,那不是必须的。简言之, AWK
是一种能处理从琐碎的小事到日常例行公事的强大语言。
学习 AWK 的难度要比学习其他任意语言的难度都小。如果你已经掌握了 C 语言,那么你会
发现学习 AWK 将会是如此简单和容易。 AWK 最开始由三个人开发—— A.Aho 、 B.W.Kernighan 和 P.Weinberger 。所有 AWK 的名字来
自他们名字的第一个字母。
下面是 AWK 的几个变种:
AWK 是最原始的 AWK 。
NAWK 是 new AWK
GAWK 是 GNU AWK 。所有 linux 发行版都默认使用 GAWK ,它和 AWK 以及 NAWK
完全兼容。
本书包含了原始 AWK 的所有基础功能,以及 GAWK 特有的一些高级功能。在安装了 NAWK
或 GAWK 的操作系统上,你仍然可以直接使用 awk 命令,它会根据情况调用 nawk 或 gawk 。
以 linux 系统为例,你会看到 awk 是一个指向 gawk 的符号链接,所以在 linux 上执行 awk 或
gawk 将会调用 gawk :
$ ls -l /bin/awk /bin/gawk
lrwxrwxrwx 1 root root 4 Apr 8 2011 /bin/awk -> gawk
-rwxr-xr-x 1 root root 319336 Dec 3 2008 /bin/gawk
本书示例将用到下面三个文件,请先建立它们,然后用它们来运行所有示例。
employee.txt 文件
employee.txt 文件以逗号作为字段分界符,包含 5 个雇员的记录,其格式如下:
employee-number,employee-name,employee-title
建立该文件 :
$ vi employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager
items.txt 文件
items.txt 是一个以逗号作为字段分界符的文本文件,包含 5 条记录,其格式如下:
items-number,item-description,item-category,cost,quantity-available
建立该文件 :
$ vi items.txt
101,HD Camcorder,Video,210,10
102,Refrigerator,Appliance,850,2
103,MP3 Player,Audio,270,15
104,Tennis Racket,Sports,190,20
105,Laser Printer,Office,475,5
items-sold.txt 文件 items-sold.txt 是一个以空格作为字段分界符的文本文件,包含 5 条记录。每条记录都是特定
商品的编号以及当月的销售量 (6 个月 ) 。因此每条记录有 7 个字段。第一个字段是商品编号,
第二个字段到第七个字段是 6 个月内每月的销售量。其格式如下:
item-number qty-sold-month1 qty-sold-month2 qty-sold-month3 qty-sold-month4
qty-sold-month5 qty-sold-month6
建立该文件 :
$ cat items-sold.txt
101 2 10 5 8 10 12
102 0 1 4 3 0 2
103 10 6 11 20 5 13
104 2 3 4 0 6 5
105 10 2 5 7 12 6
51.Awk 命令语法
Awk 基础语法 :
Awk –Fs ‘/pattern/ {action}’ input-file
(或者)
Awk –Fs ‘{action}’ input-file
上面语法中:
z -F 为字段分界符。如果不指定,默认会使用空格作为分界符。
z /pattern/ 和 {action} 需要用单引号引起来。
z /pattern/ 是可选的。如果不指定, awk 将处理输入文件中的所有记录。如果指定一
个模式, awk 则只处理匹配指定的模式的记录。
z {action} 为 awk 命令,可以是单个命令,也可以多个命令。整个 action( 包括里面的
所有命令 ) 都必须放在 { 和 } 之间。
z Input-file 即为要处理的文件
下面是一个演示 awk 语法的非常简单的例子 :
$ awk -F: '/mail/ {print $1}' /etc/passwd
mail
mailnull
这个例子中 :
z -F 指定字段分界符为冒号,即各个字段以冒号分隔。请注意,你也可以把分界符
用双引号引住, -F”:” 也是正确的。
z /mail/ 指定模式, awk 只会处理包含关键字 mail 的记录
z {print $1} 动作部分,该动作只包含一个 awk 命令,它打印匹配 mail 的每条记录的
第 1 个字段
z /etc/passwd 即是输入文件
把 awk 命令放入单独的文件中(awk 脚本)当需要执行很多 awk 命令时,可以把/pattern/{action}这一部分放到单独的文件中,然后调 用它:
awk –Fs –f myscript.awk input-file
myscript.awk 可以使用任意扩展名(或者不用扩展名)。但是加上扩展名.awk 便于维护,也可
以在这个文件中设置字段分界符(后面详述),然后调用:
awk –f myscript.awk input-file
52.Awk 程序结构(BEGIN,body,END)区域
典型的 awk 程序包含下面三个区域 :
1. BEGIN 区域
Begin 区域的语法 :
BEGIN { awk-commands }
BEGIN 区域的命令只最开始、在 awk 执行 body 区域命令之前执行一次。
z BEGIN 区域很适合用来打印报文头部信息,以及用来初始化变量。
z BEGIN 区域可以有一个或多个 awk 命令
z 关键字 BEGIN 必须要用大写
z BEGIN 区域是可选的
2. body 区域
body 区域的语法 :
/pattern/ {action}
body 区域的命令每次从输入文件读取一行就会执行一次
z 如果输入文件有 10 行,那 body 区域的命令就会执行 10 次 ( 每行执行一次 )
z Body 区域没有用任何关键字表示,只有用正则模式和命令。
3. END block
END 区域的语法 :
END { awk-commands }
END 区域在 awk 执行完所有操作后执行,并且只执行一次。
z END 区域很适合打印报文结尾信息,以及做一些清理动作
z END 区域可以有一个或多个 awk 命令
z 关键字 END 必须要用大写
z END 区域是可选的
Awk 的执行流程
下面的例子包含上上述的三个区域:
$ awk 'BEGIN { FS=":";print "----header----" } \
/mail/ {print $1} \
END {print "----footer----"}' /etc/passwd
----header----
mail
mailnull
----footer----
提示:如果命令很长,即可以放到单行执行,也可以用 \ 折成多行执行。上面的例子用 \ 把命
令折成了 3 行。
在这个例子中: z BEGIN { FS=”:”;print “----header----“ } 为 BEGIN 区域,它设置了字段分界符变量 FS( 下
文详述 ) 的值,然后打印报文头部信息。这个区域仅在 body 区域循环之前执行一次。
z /mail/{print $1} 是 body 区域,包含一个正则模式和一个动作,即在输入文件中搜索
包含关键字 mail 的行,并打印第一个字段。
z END {print “----footer----“ } 是 END 区域,打印报文尾部信息。
z /etc/passwd 是输入文件,每行记录都会执行一次 body 区域里的动作。
上面的例子中,除了可以在命令行上执行外,还可以通过脚本执行。
首先建立下面的文件 myscript.awk, 它包含了 begin,body 和 end :
$vi myscript.awk
BEGIN {
FS=":"
print "---header---"
}
/mail/ {
print $1
}
END {
print "---footer---"
}
然后,如下所示,在 /etc/passwd 上执行 myscript.awk 文件:
$awk -f myscript.awk /etc/passwd
---header---
mail
---footer---
请注意, awk 脚本中,注释以 # 开头。如果要编写复杂的 awk 脚本,最后接受下面的建议:
在 *awk 文件中写上足够多的注释,这样以后再次使用该脚本时,更易于读懂。
下面是随机列出的一些简单的例子,用例演示 awk 各个区域的不同组合方式:
只有 body 区域 :
awk –F: ‘{ print $1 }’ /etc/passwd
同时具有 begin,body 和 end 区域 :
awk –F: ‘BEGIN{printf “username\n-------\n”}\
{ print $1 }\
END {print “----------“ }’ /etc/passwd
只有 begin 和 body 区域 :
Awk –F: ‘BEGIN {print “UID”} {print $3}’ /etc/passwd
关于使用 BEGIN 区域的提示 :
只使用 BEGIN 区域在 awk 中是符合语法的。在没有使用 body 区域时,不需要指定输入文件,
因为 body 区域只在输入文件上执行。所以在执行和输入文件无关的工作时,可以只使用
BEGIN 区域。下面的不少例子中,只包含 BEGIN 区域,用来说明 awk 的不同部分是如何执行
的。你可以因地制宜地使用下面的例子。
只包含 BEGIN 的简单示例 :
$awk ‘BEGIN { print “Hello,World!” }’
Hello World!
多个输入文件 :
注意,可以为 awk 指定多个输入文件。如果指定了两个文件,那么 body 区域会首先在第一
个文件的所有行上执行,然后在第二个文件的所有行上执行。
多个输入文件示例 :
$awk 'BEING { FS=":";print "---header---" }\
/mail/ {print $1}\
END { print "---footer---"}' /etc/passwd /etc/group
mail:x:8:12:Mailer
mail:x:12:
maildrop:!:59:
---footer---
注意,即是指定了多个文件, BEGIN 和 END 区域,仍然只会执行一次。
53.打印命令
默认情况下, awk 的打印命令 print( 不带任何参数 ) 会打印整行数据。下面的例子等价于 ”cat
employee.txt” 命令 .
$awk '{print}' employee.txt
101,John Doe,CEO
102,Jason Smith,IT Manager
103,Raj Reddy,Sysadmin
104,Anand Ram,Developer
105,Jane Miller,Sales Manager
也可以通过传递变量 ”$ 字段序号 ” 作为 print 的参数来指定要打印的字段。我们猜想例子应该
只打印雇员名称 ( 第 2 个字段 )
$awk '{print $2}' employee.txt
Doe,CEO
Smith,IT
Reddy,Sysadmin
Ram,Developer
Miller,Sales
等等,这个输出好像和预期不符。它打印了从姓氏开始直到记录结尾的所有内容。这是因为
awk 默认的字段分隔符是空格, awk 准确地执行了我们要求的动作,它以空格作为分隔符,
打印第 2 个字段。当使用默认的空格作为字段分隔符时, 101,Johne 变成了第一条记录的第
一个字段, Doe,CEO 变成了第二个字段。因此上面例子中, awk 把 Doe,CEO 作为第二个字段
打印出来了。
要解决这个文件,应该使用-F 选项为 awk 指定一个逗号”,”最为字段分隔符。
$awk -F ',' '{print $2}' employee.txt
John Doe Jason Smith
Raj Reddy
Anand Ram
Jane Miller
当字段分隔符是单个字符时,下面的所有写法都是争取的,即可以把它放在单引号或双引号
中,或者不使用引号:
awk –F ‘,’ ‘{print $2}’ employee.txt
awk –F “,” ‘{print $2}’ employee.txt
awk –F , ‘{print $2}’ employee.txt
ᨀ示 : 也可使用 FS 变量来达到同样的目的。后面会介绍这个 awk 内置变量的用法。
一个简单的例子,用来输出雇员姓名,职位,同时附带 header 和 footer 信息 :
$awk 'BEGIN{FS=",";print "---------\nName Title\n------------\n";}\
{print $2,"\t",$3}\
END {print "-------------------"}' employee.txt
---------
Name Title
------------
John Doe CEO
Jason Smith IT Manager
Raj Reddy Sysadmin
Anand Ram Developer
Jane Miller Sale
这个例子中,输出结果各字段并没有很好地对齐,后面章节将会介绍如何处理这个问题。这
个例子还展示了如何使用 BEGIN 来打印 header 以及如何使用 END 来打印 footer.
请注意, $0 代表整条记录。下面两个命令是等价的,都打印 employee.txt 的所有行 :
awk ‘{print}’ employee.txt
awk ‘{print $0}’ employee.txt
54.模式匹配
你可以只在匹配特殊模式的行数执行 awk 命令。
下面的例子只打印管理者的姓名和职位:
$awk -F ',' '/Manager/ {print $2,$3}' employee.txt
Jason Smith IT Manager
Jane Miller Sales Manager
下面的例子只打印雇员 id 为 102 的雇员的信息:
$awk -F ',' '/^102/{print "Emp id 102 is",$2}' \
> employee.txt
Emp id 102 is Jason Smith
资料来源于《SedandAwk101Hacks》,大家有兴趣可以买一本,也可以关注我,我更新完它。
曾经,我花费大半月将它们跑完,现在啥都忘了,还是要常用。
只为学习交流,不为获利,侵权联系立删。