文本处理三剑客之awk

专题文章 云天 6个月前 (10-28) 288次浏览 0个评论

文本处理三剑客之awk

“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命令分为几个部分,以上面的命令为例,解释如下:

  1. awk为命令本身
  2. [options] 是可选的命令选项,如-F指定分隔符,-v指定用户自定义变量等。
  3. ‘program’ 是awk脚本代码,一般包含两部分,即匹配模式和打印模式,将符合匹配模式的内容使用打印语句打印出来。这里使用单引号将代码括起来,程序代码中可以使用if、for、while等控制语句,以及各种awk函数以及各种内置变量。也可以将awk脚本代码保存到文件中,然后使用-f选项指定文件路径。
  4. file(s) 是awk处理的文件,可以指定多个文件,也可以使用通配符。
 # awk命令的基本语法示例
 awk -F'|' '/5/{print}' demo.txt

以上命令解释如下:

  1. awk为命令本身
  2. -F’|’ 是选项部分,这里使用-F选项指定分隔符为’|’
  3. ‘/5/’ 指定匹配模式,即匹配文本中包含’5’的行
  4. ‘{print}’ 指定打印模式,即打印匹配到的行
  5. 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

6. 统计文件中每行出现的字符数量:

 awk '{for(i=1;i<=length();i++) a[substr($0,i,1)]++} END{for(i in a) print i,a[i]}' demo.txt
喜欢 (1)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址