“awk是一个强大的文本处理工具”,许多文章都会如此描述,可是他到底有多强大呢,仅仅是简单的使用分隔符对文本进行切分吗?带着这个问题,我们来深入探究一下awk命令。 awk最早在uninx时代就已经存在,在后来的linux系统中,又出现了增强版本nawk(new awk)、gawk(GNU awk)等,目前我们经常使用的awk命令一般就是gawk,实际上是在系统中使用软连接的方式将awk指向gawk。
一、awk命令的语法 使用liunx自带的命令手册可以查看awk命令的基本语法:
man awk
SYNOPSIS
gawk [ POSIX or GNU style options ] -f program-file [ -- ] file ...
gawk [ POSIX or GNU style options ] [ -- ] program-text file ...
从手册中可以看到,awk命令有两种使用方式,一种是通过-f选项指定awk脚本文件,另一种是直接在命令行中输入awk脚本代码。在实际使用中我们通常使用直接输入脚本代码的方式,这里也将介绍这种方式。
# awk命令的基本语法
awk [options] 'program' file(s)
awk命令分为几个部分,以上面的命令为例,解释如下:
- awk为命令本身
- [options] 是可选的命令选项,如-F指定分隔符,-v指定用户自定义变量等。
- ‘program’ 是awk脚本代码,一般包含两部分,即匹配模式和打印模式,将符合匹配模式的内容使用打印语句打印出来。这里使用单引号将代码括起来,程序代码中可以使用if、for、while等控制语句,以及各种awk函数以及各种内置变量。也可以将awk脚本代码保存到文件中,然后使用-f选项指定文件路径。
- file(s) 是awk处理的文件,可以指定多个文件,也可以使用通配符。
# awk命令的基本语法示例
awk -F'|' '/5/{print}' demo.txt
以上命令解释如下:
- awk为命令本身
- -F’|’ 是选项部分,这里使用-F选项指定分隔符为’|’
- ‘/5/’ 指定匹配模式,即匹配文本中包含’5’的行
- ‘{print}’ 指定打印模式,即打印匹配到的行
- demo.txt 是文件名,可以指定多个文件,也可以使用通配符如*代表当前目录下所有文件。
以下将对awk命令的各部分进行介绍。
二、awk命令选项部分 这里只介绍常用选项,详细的选项请参考手册。 -F选项用于指定分隔符,默认情况下awk以空格、制表符、换行符作为分隔符,如果需要指定其他分隔符,可以使用-F选项。
# 指定分隔符为'|'
awk -F'|' '{print}' demo.txt
# 指定分隔符为'|'
awk -F\| '{print}' demo.txt
# 指定分隔符为'.'
awk -F. '{print}' demo.txt
-v选项用于指定用户自定义变量,可以将awk脚本中的变量值在运行时赋值。
# 定义变量a
awk -v var=99 'BEGIN {print var}'
-f选项用于指定awk脚本文件,可以将awk脚本代码保存到文件中,然后使用-f选项指定文件路径,awk脚本具体写法本文暂不涉及。
# 使用-f选项指定文件路径
awk -f test.awk demo.txt
三、awk命令program部分 (一)匹配模式 awk提供了多种匹配模式,如正则表达式、关系表达式、逻辑表达式等,详细如下 空模式 空模式就是不使用任何匹配模式,即使用默认分隔符、换行符等对文件进行操作
# 使用默认分隔符、换行符,空模式,输出所有行
awk '{print}' demo.txt
BEGIN 和 END 模式 BEGIN模式在awk处理文件之前执行,END模式在awk处理文件之后执行。以以下命令为例,可以看出,即使在awk命令的program部分,BEGIN、END模式和print命令没有按照”BEGIN->print->END“的顺序写,awk也会按照”BEGIN->print->END“的顺序执行。
# BEGIN模式在awk处理文件之前执行,END模式在awk处理文件之后执行
uatu@ubuntu01:~/learn/demo/awk$ awk -F'|' ' {print FILENAME} END {print "the after"} BEGIN {print "the begin"}' demo.txt
the begin
demo.txt
demo.txt
demo.txt
the after
行范围模式 是指在awk命令中,可以指定处理文件的行范围,如处理第10行到第20行,或者处理第2行到最后一行,或者两个匹配模式中内容之间的所有行等。
# 处理第10行到第20行
awk 'NR>=10 && NR<=20{print}' demo.txt
# 处理第2行到最后一行
awk 'NR>=2{print}' demo.txt
# 处理两个匹配模式之间的所有行(不含pattern2)
awk '/pattern1/,/pattern2/{print}' demo.txt
# 处理两个匹配模式所属的行
awk '/pattern1/{print} /pattern2/{print}' demo.txt
正则表达式
# 匹配文本中以five开头的行,其中^表示行首
awk '/^five/{print}' demo.txt
# 匹配文本中以三个数字结尾的行
awk '/\d{3}$/{print}' demo.txt
# 匹配文本中包含邮箱的行,如abc@qq.com
awk '/@qq.com/{print}' demo.txt
运算表达式
1. 关系表达式 支持的关系表达式有:==、!=、<、>、<=、>=、、!等。
# 匹配文本第五行之后的行的前两列
awk 'NR>5{print $1,$2}' demo.txt
# 匹配文本第五行之前的行的前两列
awk 'NR<5{print $1,$2}' demo.txt
# 匹配文本第3行等于5的行
awk '$3=5{print}' demo.txt
# 匹配文本中包含'5'的行
awk '$0 ~ /5/{print}' demo.txt
# 匹配文本中不包含'5'的行
awk '$0 !~ /5/{print}' demo.txt
2. 赋值表达式 awk支持的赋值表达式有:=、+=、-=、*=、/=、%=、^=等。
# 输出99的2次方
awk -v var=99 'BEGIN {print var^=2}'
# 匹配文本中包含'5'的行,并将匹配到的行的第三列赋值给变量a
awk '$0 ~ /5/{a=$3;print a}' demo.txt
# 将匹配到的行的第三列加1,
awk '{a=3;a+=1;print a}' demo.txt
3. 算术表达式 awk支持的算术表达式有:+、-、*、/、%、++、–等。
# 匹配文本中包含'5'的行,并将匹配到的行的第三列加1
awk '$0 ~ /5/{a=$3+1;print a}' demo.txt
# 匹配文本中包含'5'的行,并将匹配到的行的第三列减1
awk '$0 ~ /5/{a=$3-1;print a}' demo.txt
# 匹配文本中包含'5'的行,并将匹配到的行的第三列乘2
awk '$0 ~ /5/{a=$3*2;print a}' demo.txt
# 输出99+1
awk -v var=99 'BEGIN {var++;print var}'
4. 逻辑表达式 awk支持的逻辑表达式有:&&、||、!等。
# 匹配文本中包含'5'并且以'a'结尾的行
awk '$0 ~ /5.*a/{print}' demo.txt
5. 布尔值 awk中提供了布尔值,其中,数字类,非0为true,0为false。字符串类型,非空字符串为true,空字符串为false。
# 数字类,1 && 1,结果是1
awk -v var=99 'BEGIN {print 1&&1}'
# 字符串类型,"str" && "",结果是0
awk -v var=99 'BEGIN {print "str" && ""}'
(二)打印模式 awk提供了各种打印语句,如print、printf、getline等,详细如下 print语句 print语句用于打印匹配到的行,默认情况下,print语句会打印每一行,如果需要打印指定列,可以使用$n表示列,如$1表示第一列,$2表示第二列,以此类推。
# 打印所有行
awk '{print}' demo.txt
# 打印所有行的第一列
awk '{print $1}' demo.txt
# 打印所有行的第一列和第三列
awk '{print $1,$3}' demo.txt
# 打印所有行的第一列和第三列,并指定分隔符为'|'
awk -F'|' '{print $1,$3}' demo.txt
printf语句 printf语句用于格式化打印输出,可以指定输出格式,如%d表示整数,%s表示字符串,%f表示浮点数等。
# 打印所有行的第一列,并指定输出格式为整数
awk '{printf "%d\n", $1}' demo.txt
# 打印所有行的第一列和第三列,并指定输出格式为字符串
awk '{printf "%s %s\n", $1,$3}' demo.txt
# 打印所有行的第一列和第三列,并指定输出格式为浮点数
awk '{printf "%f %f\n", $1,$3}' demo.txt
getline语句 getline语句用于从文件中读取一行,并将其赋值给变量,如果文件已经读取完毕,则返回0。
# 读取文件demo.txt的第一行,并将其赋值给变量line
awk 'NR==1{getline line;print line}' demo.txt
(三)控制语句 awk中可以使用多种控制语句,如if、for、while等,详细如下 if语句 if语句用于条件判断,语法如下:
awk 'BEGIN{ for(i in $1){ print}}'
for语句 for语句用于循环,语法如下:
awk -F: '{a[$1]+=2}END{for(i in a){print i,a[i]}}' /etc/passwd
while语句 while语句用于循环,语法如下:
awk -F: '{i=0;while(i<6){print $1;i++}}' /etc/passwd
next语句 next语句用于跳过当前循环,语法如下:
# 从passwd中取redis用户信息到ps.txt
grep redis /etc/passwd > ps.txt
# 对ps.txt文件中每行匹配的数据各循环输出10次,i为3时跳过此次循环
awk -F: '{ for(i=0;i<10;i++){if(i==3)next;print i,$1} }' ps.txt
break语句 break语句用于终止当前循环,语法如下:
# print 0-9,i为3时结束循环
awk 'BEGIN{ for(i=0;i<10;i++){if(i==3)break;print i} }'
continue语句 continue语句用于跳过当前循环的剩余语句,语法如下:
# print 0-9,i为3时不print
awk 'BEGIN{ for(i=0;i<10;i++){if(i==3)continue;print i} }'
(四)内置变量 awk中提供了各种内置变量,常用的列入下表,更多的变量请参考手册(man awk)。
变量名称 | 描述 |
---|---|
NF | 当前记录的字段数,即有多少列 |
NR | 当前处理的记录数,即有多少行,多个文件合并计数 |
FNR | 当前处理的文件中的记录数,多个文件分别计数 |
FS | 输入字段分隔符,默认是空格、制表符、换行符 |
OFS | 输出字段分隔符,默认是空格 |
ORS | 输出记录分隔符,每行的分隔符,默认是换行符 |
RS | 输入记录分隔符,每行的分隔符,默认是换行符 |
RSTART | 当前匹配的起始位置 |
RLENGTH | 当前匹配的长度 |
FILENAME | 当前处理的文件名 |
OFMT | 输出格式,默认是%.6g |
ARGC | 命令行参数个数 |
ARGV | 命令行参数数组 |
FILENAME | 当前处理的文件名 |
四、awk命令的使用实例
1. 更换分隔符
# 将文件demo.txt中的每行以'|'分割,更改分隔符为^之后,保存到demo3.txt中
awk -F\| -v OFS="^" '{print $1,$2,$3}' demo.txt > demo3.txt
2. 取文件中指定列数据
# 取文件demo.txt的第一列数据,使用默认分隔符
awk '{print $1}' demo.txt
3. 将文件中指定列数据加总
# 取文件demo.txt的第一列和第三列数据,并将两列数据加总,保存到文件sum.txt中
awk '{sum+=$1} END {print sum}' demo.txt
4. 统计文件中单词出现的次数
# 统计文件demo.txt中单词出现的次数,并保存到文件word.txt中
awk '{for(i=1;i<=NF;i++) a[$i]++} END{for(i in a) print i,a[i]}' demo.txt > word.txt
5. 统计文件中每行出现的单词数量:
awk '{for(i=1;i<=NF;i++) a[$i]++} END{for(i in a) print i,a[i]}' demo.txt
awk '{for(i=1;i<=length();i++) a[substr($0,i,1)]++} END{for(i in a) print i,a[i]}' demo.txt