学习资料:《linux大棚命令百篇上》
awk介绍及简单实用
以下是在网上找到的两个awk的介绍:
- awk是一种处理文本文件的语言,是一个强大的文本分析工具。(来自http://www.runoob.com/linux/linux-comm-awk.html)
- awk是一种编程语言,用于在linux/unix下对文本和数据进行处理。数据可以来自标准输入(stdin)、一个或多个文件,或其它命令的输出。它支持用户自定义函数和动态正则表达式等先进功能,是linux/unix下的一个强大编程工具。它在命令行中使用,但更多是作为脚本来使用。awk有很多内建的功能,比如数组、函数等,这是它和C语言的相同之处,灵活性是awk最大的优势。(来自: http://man.linuxde.net/awk)
- awk的名字由来是取自创始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首个字母
awk的命令格式和选项
格式:awk [Options] 'Pattern {Actioin}' filename
说明:
-
Option
部分,用来指定命令选项 -
Pattern
部分,用来指定判断条件,相当于我们之前说sed的行匹配语句。 -
Action
部分,用于指定awk的动作,也就是对Pattern
选中行执行的操作。 -
filename
部分,用于指定awk要操作的文件名
举个栗子
通过以下awk命令可以将一个文件全部输出,效果同cat student
。
wangsheng@ubuntu[14:44:40]:~/Documents$ awk '{print $0}' student
Marry 2143 78 84 77
Jack 2321 66 78 45
Tom 2122 48 77 71
Mike 2537 87 97 95
Bob 2415 40 57 62
也可以输出第1列和第3列
wangsheng@ubuntu[14:45:04]:~/Documents$ awk '{print $1,$3}' student
Marry 78
Jack 66
Tom 48
Mike 87
Bob 40
awk的工作原理
来自: http://man.linuxde.net/awk
(以下摘抄自《linux大棚命令百篇》)
- awk读取一条记录(文件中一行内容)作为输入,并将这条记录赋值给内部变量$0。
- 记录被分符分割成多个字段,每一个字段都存储到指定编号的变量中,从$1开始。(awk内部变量FS用来指定字段的分隔符,默认情况下为空格)
- 对于每一条记录,按照规定的pattern进行匹配,如果匹配成功,则执行对应的action,否则不执行。pattern和action都是可选的,但两者必须提供一个。
- 如果未指定pattern,则对所有行都执行action操作。
- 如果未指定action,则对指定行执行{print}操作,打印匹配行的内容。
- 如果action指定为{},则不做任何操作。
awk的各个组成部分详解
option
options用来指定awk的选项,到底有哪些选项呢?我们一个一个来看看
-
-F fs
fs指定输入分隔符,可以是字符串和正则表达式
# 文件内容
wangsheng@ubuntu[14:53:33]:~/Documents$ cat log
hello,world,java,linux,android
# 不使用-F
wangsheng@ubuntu[14:51:51]:~/Documents$ awk '{print $1,$3}' log
hello,world,java,linux,android
# 使用-F
wangsheng@ubuntu[14:52:19]:~/Documents$ awk -F , '{print $1,$3}' log
hello java
-
-v var=value
定义变量var,值为value(注意赋值语句中间不能有空格)
# 定义一个变量并输出a+文件内容
wangsheng@ubuntu[14:57:27]:~/Documents$ awk -v a=1 '{print a$0}' log
1hello,world,java,linux,android
# 定义两个变量时,每个变量前加—v
wangsheng@ubuntu[14:58:10]:~/Documents$ awk -v a=1000 -v b=2000 '{print a$0b}' log
1000hello,world,java,linux,android2000
-
-f scriptfile
从脚本中读取命令,当命令部分过长时,将命令部分单独放到一个脚本文件中,通过这种方式调用,跟sed里的用法是一样的,后面我们用到在举例说明。
pattern
说完了选项,我们来说说这个pattern,它也是可选的,表示匹配哪一行。如果没有提供pattern语句块,则默认执行{ print },即打印每一个读取到的行,awk读取的每一行都会执行该语句块。pattern可以包含如下几项:
-
/正则表达式/
,这个不用说了,最方便的匹配操作。
# 打印含有Jack的行
wangsheng@ubuntu[15:14:16]:~/Documents$ awk '/Jack/ {print}' student
Jack 2321 66 78 45
-
关系表达式
,用关系表达式的真假来判断要不要执行后面的操作。
# NR是awk的一个内置变量,代表行号
# 用NR%2==0这个关系表达式这个模式
# 当结果为true时,执行{next}
# 当结果为false时,执行{print NR,$0},将行号与该行内容输出
wangsheng@ubuntu[15:14:50]:~/Documents$ awk 'NR%2==0 {next} {print NR,$0}' student
1 Marry 2143 78 84 77
3 Tom 2122 48 77 71
5 Bob 2415 40 57 62
-
模式匹配表达式
,用~
表示匹配正则表达式、!~
表示不匹配正则表达式。
# 选取第1列(名字)中不含有o的那些行,打印该行内容
wangsheng@ubuntu[15:25:35]:~/Documents$ awk '$1 !~ /o/ {print}' student
Marry 2143 78 84 77
Jack 2321 66 78 45
Mike 2537 87 97 95
# 选取第1列中含有o的那些行,打印该行内容
wangsheng@ubuntu[15:26:45]:~/Documents$ awk '$1 ~ /o/ {print}' student
Tom 2122 48 77 71
Bob 2415 40 57 62
-
BEGIN{} pattern{} END{}
,这是awk中使用比较普遍的一种形式,BEGIN、END分别表示在开始读入第一行前和读入最后一行结束后的操作。
- BEGIN语句块:可选的语句块,在awk开始从输入流中读取行之前被执行,比如变量初始化、打印输出表格的表头等语句通常可以写在BEGIN语句块中。
- pattern语句块:可选的语句块,是最重要的部分。如果没有提供pattern语句块,则默认执行{ print },即打印每一个读取到的行,awk读取的每一行都会执行该语句块。
- END语句块:可选的语句块,在awk从输入流中读取完所有的行之后即被执行,比如打印所有行的分析结果这类信息汇总都是在END语句块中完成。
action
常用的action当然就是print了,当然在这个action中我们能做很多事情,完全就跟用c语言编程一样,可以有条件控制、循环、赋值、数组定义使用等等,这里就不展开来说了。具体用法可以参见http://man.linuxde.net/awk。 这里要重点说一下的是awk的内置变量。
awk的内置变量
所谓内置变量就是指awk中自带的变量,不需要用-v来定义就可以使用的变量。常用的有以下几种:
- $0 表示当前行的所有内容。
- $n 表示用分割符分开后,第n个字段。
- ARGC 表示命令行参数的数目。
wangsheng@ubuntu[16:16:20]:~/Documents$ awk '{print ARGC}' log
2
- ARGV 表示命令行参数的数组。可以看到ARGV[0]是awk,而ARGV[1]-ARGV[n]都是文件名
wangsheng@ubuntu[16:17:04]:~/Documents$ awk '{print ARGV[0]}' log
awk
wangsheng@ubuntu[16:18:35]:~/Documents$ awk '{print ARGV[1]}' log
log
- FILENAME 表示当前处理文件的文件名称。
wangsheng@ubuntu[16:01:23]:~/Documents$ awk '{print FILENAME}' log
log
wangsheng@ubuntu[21:58:04]:~/Documents$ awk '{print FILENAME}' log student
log
student
student
student
student
student
- FS 表示字段的分隔符,awk在输入时按FS指定的符号进行字段分割。
wangsheng@ubuntu[16:02:05]:~/Documents$ awk -F , '{print FS}' log
,
- OFS 输出字段分隔符(默认值是一个空格),awk输出时使用OFS指定的符号进行字段拼接。
wangsheng@ubuntu[22:02:02]:~/Documents$ awk -F , 'BEGIN{OFS="-"} {print $1,$2,$3}' log
hello-world-java
- NR 表示已读记录数,无论有多少个文件,只要读入一条记录,该值就会加1,注意与FNR的不同之处。
wangsheng@ubuntu[22:02:11]:~/Documents$ awk '{print NR}' log student
1
2
3
4
5
6
- FNR 当前文件已读的记录数,操作当前文件时,每读入一条记录,FNR就会加1,更换文件后,该变量会从0重新计数。
wangsheng@ubuntu[22:03:24]:~/Documents$ awk '{print FNR}' log student
1
1
2
3
4
5
处理多个文件
- 当要处理两个文件的时候,如何判断当前操作的是哪个文件呢?其实看了之前awk的内置变量,答案并不是很难,直接通过FNR与NR的关系就可以了。当操作的是第一个文件的时候,FNR==NR;当操作的是第二个文件的时候,FNR<NR。
wangsheng@ubuntu[22:03:46]:~/Documents$ awk 'FNR==NR {print "this is first file"} FNR<NR {print "this is second file"}' log student
this is first file
this is second file
this is second file
this is second file
this is second file
this is second file
- 当要处理的有三个文件的时候怎么办呢?上面这个办法显然不能解决问题,它只能判断是否是第一个文件,所以当判断三个或三个以上的文件的时候,可以使用FILENAME与ARGS数组,当FILENAME==ARGV[1]时,代表当前操作的是第一个文件,FILENAME==ARGV[2]的时候是第二个以此类推。
wangsheng@ubuntu[22:17:21]:~/Documents$ awk 'FILENAME==ARGV[1] {print "1--"} FILENAME==ARGV[2] {print "2----"} FILENAME==ARGV[3] {print "3---"}' log student hello
1--
2----
2----
2----
2----
2----
3---
3---
awk脚本
还记得我们之前说awk -f选项的作用吗?看之前的代码也能看出来,awk的中间部分是很长的,如果每次都在命令行敲难免有些麻烦,将其放在文件中保存,使用的时候直接调用文件名,这样就很方便了,-f选项就是代表我写的awk中间的pattern {action}
部分在后面的脚本文件中。
首先创建一个awk脚本文件script.awk,注意文件开头是#!/bin/awk -f
#!/bin/awk -f
#运行前
BEGIN {
math = 0
english = 0
computer = 0
printf "NAME NO. MATH ENGLISH COMPUTER TOTAL\n"
printf "---------------------------------------------\n"
}
#运行中
{
math+=$3
english+=$4
computer+=$5
printf "%-6s %-6s %4d %8d %8d %8d\n", $1, $2, $3,$4,$5, $3+$4+$5
}
#运行后
END {
printf "---------------------------------------------\n"
printf " TOTAL:%10d %8d %8d \n", math, english, computer
printf "AVERAGE:%10.2f %8.2f %8.2f\n", math/NR, english/NR, computer/NR
}
脚本在BEGIN部分给三个变量赋值,接着打印一个表头;中间对每行进行累加第3、4、5列数值的操作;END部分将最终处理的值打印出来。
运行脚本:
//源文件
wangsheng@ubuntu[22:30:01]:~/Documents$ cat student
Marry 2143 78 84 77
Jack 2321 66 78 45
Tom 2122 48 77 71
Mike 2537 87 97 95
Bob 2415 40 57 62
//运行后的结果
wangsheng@ubuntu[22:30:03]:~/Documents$ awk -f script.awk student
NAME NO. MATH ENGLISH COMPUTER TOTAL
---------------------------------------------
Marry 2143 78 84 77 239
Jack 2321 66 78 45 189
Tom 2122 48 77 71 196
Mike 2537 87 97 95 279
Bob 2415 40 57 62 159
---------------------------------------------
TOTAL: 319 393 350
AVERAGE: 63.80 78.60 70.00
格式化输出
格式化输出用于将数据按照我们想要的格式进行输出,而不是直接将数据打印出来。
格式 | 描述 |
---|---|
%d | 十进制有符号整数 |
%u | 十进制无符号整数 |
%f | 浮点数 |
%s | 字符串 |
%c | 单个字符 |
%p | 指针的值 |
%e | 指数形式的浮点数 |
%x | %X 无符号以十六进制表示的整数 |
%o | 无符号以八进制表示的整数 |
%g | 自动选择合适的表示法 |
看几个例子:
wangsheng@ubuntu[22:55:50]:~/Documents$ awk 'BEGIN{a=124.113;printf("%-10.2f",a)}'
124.11 wangsheng@ubuntu[22:55:55]:~/Documents$
%-10.2f f表示浮点数,.2表示小数部分保留两位,10表示总共占10个空白字符空间,-表示左对齐
wangsheng@ubuntu[22:59:24]:~/Documents$ awk 'BEGIN{a=90;printf("%10X",a)}'
5Awangsheng@ubuntu[22:59:31]:~/Documents$
%10X X表示转为十六进制数,10表示总共占10个空白字符长度
wangsheng@ubuntu[23:03:33]:~/Documents$ cat log
hello,world,java,linux,android
wangsheng@ubuntu[23:03:35]:~/Documents$ awk -F , '{printf("%-10s---%10s---%-10s---%10s",$1,$2,$3,$4)}' log
hello --- world---java --- linuxwangsheng@ubuntu[23:03:38]:~/Documents$
将log文件根据,分割开,分别对第1、2、3、4列打印输出,有的左对齐,有的右对齐,均占10个空白字符
参考资料
http://man.linuxde.net/awk
http://www.runoob.com/linux/linux-comm-awk.html
http://www.mamicode.com/info-detail-1594530.html
网友评论