linux命令学习(三)——awk

学习资料:《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
说明

  1. Option部分,用来指定命令选项
  2. Pattern部分,用来指定判断条件,相当于我们之前说sed的行匹配语句。
  3. Action部分,用于指定awk的动作,也就是对Pattern选中行执行的操作。
  4. 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大棚命令百篇》)

  1. awk读取一条记录(文件中一行内容)作为输入,并将这条记录赋值给内部变量$0。
  2. 记录被分符分割成多个字段,每一个字段都存储到指定编号的变量中,从$1开始。(awk内部变量FS用来指定字段的分隔符,默认情况下为空格)
  3. 对于每一条记录,按照规定的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

处理多个文件

  1. 当要处理两个文件的时候,如何判断当前操作的是哪个文件呢?其实看了之前awk的内置变量,答案并不是很难,直接通过FNR与NR的关系就可以了。当操作的是第一个文件的时候,FNR==NR;当操作的是第二个文件的时候,FNR
wangsheng@ubuntu[22:03:46]:~/Documents$ awk 'FNR==NR {print "this is first file"} FNR
  1. 当要处理的有三个文件的时候怎么办呢?上面这个办法显然不能解决问题,它只能判断是否是第一个文件,所以当判断三个或三个以上的文件的时候,可以使用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

你可能感兴趣的:(linux命令学习(三)——awk)