AWK是贝尔实验室1977年搞出来的文本出现神器。
awk是一种用于处理文本、模式匹配的编程语言。与sed和grep,俗称Linux下的三剑客。相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大。简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理。
awk有3个不同版本: awk、nawk和gawk,未作特别说明,一般指gawk,gawk 是 AWK 的 GNU 版本。
awk其名称得自于它的创始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首个字母。
要学AWK,就得提一提AWK的一本相当经典的书《The AWK Programming Language》,它在豆瓣上的评分是9.4分!
基础用法
格式:awk 执行的事件 文件
root@xiaocan:~# awk '{print $0}' test.txt
My first language:Python
My second language:Shell
My third language:Java
My fourth language:C
其中,print 表示打印,$0
表示一整个记录,test.txt 表示一个文件。所以
awk '{print $0}' test.txt
表示把 test.txt 文件里面的每行记录都打印出来。
# $0 表示整个记录,不过 $1, $2, $3.....则表示整个记录中的第一个字段,第二个字段......。
root@xiaocan:~# awk '{print $1}' test.txt
My
My
My
My
root@xiaocan:~# awk '{print $2}' test.txt
first
second
third
fourth
root@xiaocan:~# awk '{print $3}' test.txt
language:Python
language:Shell
language:Java
language:C
下面我们用 :
来作为我们的分隔符吧。
root@xiaocan:~# awk 'BEGIN{FS=":"} {print $2}' text.txt
上面的命令等价于:
root@xiaocan:~# awk -F ':' '{print $2}' test.txt
Python
Shell
Java
C
上面我们用参数 -F
指定了分隔符。
可以指定多个分隔符:
awk -F '[;:]'
我们再来看看 awk 的格式化输出,和 C 语言的 printf 没什么两样:
root@xiaocan:~# awk -F: '{printf "%-20s -- %s\n",$1,$2}' awkTest.txt
My first language -- Python
My second language -- Shell
My third language -- Java
My fourth language -- C
条件限制
# 打印第二个字段为"Java"的文本
root@xiaocan:~# awk -F ':' '$2 == "Java" {print $2}' test.txt
Java
#打印奇数行的的第二个字段:
root@xiaocan:~# awk -F ':' 'NR % 2 == 1 {print $2}' test.txt
Python
Java
#或者
root@xiaocan:~# awk -F ':' 'NR % 2 == 1' test.txt
My first language:Java
My third language:python
其中,NR
是一个内置的变量,表示当前正在处理的记录,即当前的记录是第几个记录。
其中的“==”为比较运算符。其他比较运算符:!=, >, <, >=, <=
字符串匹配
# 匹配第三列有 Java 的行
root@xiaocan:~# awk '$3 ~ /Java/' test.txt
My first language:Java
~
表示模式开始。/ /
中是模式。这就是一个正则表达式的匹配。
其实 awk 可以像 grep 一样的去匹配第一行,就像这样:
root@xiaocan:~# awk '/Java/' test.txt
My first language:Java
root@xiaocan:~# awk '/Java|Python/' test.txt
My first language:Python
My third language:Java
模式取反:
root@xiaocan:~# awk '!/Java/' test.txt
My first language:Python
My second language:Shell
My fourth language:C
更多正则表达式匹配,参看这篇文章。
拆分文件
awk拆分文件很简单,使用重定向符号 >
就好了。下面这个例子,是按第 2 列分隔文件,相当的简单。
$ awk '{print > $2}' test.txt
$ ls
C Java Python Shell
条件语句
和我们平常的编程一样,awk 也提供了 if, else, while 等这些条件语句。
例如,打印第二个及其之后的记录:
root@xiaocan:~# awk '{if(NR > 1) print $2}' test.txt
second
third
fourth
# 或者
root@xiaocan:~# awk 'NR > 1 {print $2}' test.txt
注意,上面的字段分隔符是空格了,并且 if 语句是在“{}” 里指定的。
root@xiaocan:~# awk '{if($1 < "s") print $1; else print $2}' test.txt
# 如果第一个字段小于“s",则打印第一个字段,否则打印第二个字段
My
My
My
My
函数
awk 提供了一些内置函数来供我们使用,一下常用的函数如下:
tolower():字符转为小写。
toupper():字符转为大写
length():返回字符串长度。
substr():返回子字符串。
sqrt():平方根。
rand():随机数
root@xiaocan:~# awk '{print toupper($1)}' test.txt
MY
MY
MY
MY
root@xiaocan:~# awk '{print tolower($1)}' test.txt
my
my
my
my
root@xiaocan:~# awk -F ':' '{print toupper($2)}' test.txt
PYTHON
SHELL
JAVA
C
root@xiaocan:~# awk -F ':' '{print tolower($2)}' test.txt
python
shell
java
c
变量
常用的内置变量如下:
$0
当前记录(这个变量中存放着整个行的内容)
$1~$n
当前记录的第n个字段,字段间由FS分隔NR:表示当前处理的是第几行
NF:表示当前行有多少个字段
FNR 当前文件的行数
FILENAME:当前文件名
FS:字段分隔符,默认是空格和制表符。
RS:行分隔符,用于分割每一行,默认是换行符。
OFS:输出字段的分隔符,用于打印时分隔字段,默认为空格。
ORS:输出记录的分隔符,用于打印时分隔记录,默认为换行符。
OFMT:数字输出的格式,默认为
%.6g
。
例如我们要打印每一个记录的最后一个字段,就可以使用变量 NF 了。
root@xiaocan:~# awk '{print $NF}' test.txt
language:Python
language:Shell
language:Java
language:C
# 倒数第二个字段
root@xiaocan:~# awk '{print $(NF-1)}' test.txt
first
second
third
fourth
对了,刚才那个 NR 的变量也是挺好用的,例如:
root@xiaocan:~# awk '{print NR ". " $0}' test.txt
1. My first language:Python
2. My second language:Shell
3. My third language:Java
4. My fourth language:C
awk 脚本
awk 的语法如下:
- BEGIN{ 这里面放的是执行前的语句 }
- END {这里面放的是处理完所有的行后要执行的语句 }
- {这里面放的是处理每一行时要执行的语句}
为了说清楚这个事,我们来看看下面的示例:
假设有这么一个文件(学生成绩表):
$ cat score.txt
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
我们的 awk 脚本如下:
$ cat cal.awk
#!/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
}
我们来看一下执行结果:(也可以这样运行 ./cal.awk score.txt
)
$ awk -f cal.awk score.txt
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
统计
统计某个文件夹下的文件占用的字节数
ls -l |awk 'BEGIN {size=0;} {size=size+$5;} END{print "[end]size is ", size}'
[end]size is 8657198
如果以M为单位显示:
ls -l |awk 'BEGIN {size=0;} {size=size+$5;} END{print "[end]size is ", size/1024/1024,"M"}'
[end]size is 8.25889 M
注意,统计不包括文件夹的子目录。
环境变量
即然说到了脚本,我们来看看怎么和环境变量交互:(使用-v参数和ENVIRON,使用ENVIRON的环境变量需要export)
$ x=5
$ y=10
$ export y
$ echo $x $y
5 10
$ awk -v val=$x '{print $1, $2, $3, $4+val, $5+ENVIRON["y"]}' OFS="\t" score.txt
Marry 2143 78 89 87
Jack 2321 66 83 55
Tom 2122 48 82 81
Mike 2537 87 102 105
Bob 2415 40 62 72
最后,我们再来看几个小例子:
#从file文件中找出长度大于80的行
awk 'length>80' file
#打印99乘法表
seq 9 | sed 'H;g' | awk -v RS='' '{for(i=1;i<=NF;i++)printf("%dx%d=%d%s", i, NR, i*NR, i==NR?"\n":"\t")}'
关于其中的一些知识点可以参看gawk的手册:
- 内建变量,参看:http://www.gnu.org/software/gawk/manual/gawk.html#Built_002din-Variables
- 流控方面,参看:http://www.gnu.org/software/gawk/manual/gawk.html#Statements
- 内建函数,参看:http://www.gnu.org/software/gawk/manual/gawk.html#Built_002din
- 正则表达式,参看:http://www.gnu.org/software/gawk/manual/gawk.html#Regexp