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<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
  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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,904评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,581评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,527评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,463评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,546评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,572评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,582评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,330评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,776评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,087评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,257评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,923评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,571评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,192评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,436评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,145评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352

推荐阅读更多精彩内容

  • 转载 原文的排版和内容都更加友好,并且详细,我只是在这里贴出了一部分留作自己以后参考和学习,如希望更详细了解AWK...
    XKirk阅读 3,206评论 2 25
  • awk: grep,sed,awk grep:文本过滤 sed:文本编辑 awk:文本格式化工具; 1 什么是aw...
    木林森阅读 1,776评论 0 16
  • awk介绍awk变量printf命令:实现格式化输出操作符awk patternawk actionawk数组aw...
    哈喽别样阅读 1,562评论 0 4
  • 本章主要学习内容awk介绍 awk基本用法 awk变量 awk格式化 awk操作符 awk条件判断 a...
    楠人帮阅读 1,267评论 0 8
  • awk:报告生成器,格式化文本输出 内容: awk介绍 awk基本用法 awk变量 awk格式化 awk操作符 a...
    BossHuang阅读 1,455评论 0 9