文本处理三剑客之awk---列编辑器
awk语法
1、hello world
\\ 将This line of data is ingored写到test里面去
[root@localhost ~]# echo 'This line of data is ingored' > test
[root@localhost ~]# cat test
This line of data is ingored
\\ 不管是用awk后面跟test文件打印hello world,还是awk后面跟任意文件一个打印。语法上要打印的hello world跟文件里面的内容没关系,只不过打印的动作会因文件内容里面的行数变化而变化。
[root@localhost ~]# awk '{print "hello world"}' test
hello world
[root@localhost ~]# awk '{print "hello world"}' wjj
hello world
hello world
hello world
hello world
hello world
hello world
hello world
\\ 如果后面不跟文件的话就会卡住,然后输入任何东西(包含空格+回车、回车)都会打印hello world,
[root@localhost ~]# awk '{print "hello world"}'
q
hello world
t
hello world
y
hello world
u
hello world
hello world
\\ 文件里面明明有多行,但是只打印一行(用echo命令echo一个空行出来,空行也可以是处理对象,空行是一行,)
[root@localhost ~]# echo|awk '{print "hello world"}'
hello world
[root@localhost ~]# echo
\\ 在print语句中不加任何参数,就会打印出文件中的内容
[root@localhost ~]# awk '{print}' test
This line of data is ingored
\\ 在print后面加上$0还是打印整行(因为This就是$1,line是$2,of是$3,data是$4,is是$5,ingored是$6,在awk里面$0表示匹配awk整行文本的内容 )
[root@localhost ~]# awk '{print $0}' test
This line of data is ingored
awk分为三部分:BEGIN{}+{正文}+END{}
awk ‘BEGIN{i=1}{print “hello world”}END{print i}’ //i是变量,在awk里面,变量是不需要$符号的,直接用变量的一致去调用就可以了
// 用awk生成一个表格(如果想打印表格,就必须把表格保存在文本里面,才能打印)
[root@localhost ~]# awk 'BEGIN{print "姓名\t\t年龄\ntom\t\t10\njerry\t\t20\nzhangsan\t15\nlisi\t\t18"}'
姓名 年龄
tom 10
jerry 20
zhangsan 15
lisi 18
// 用awk统计年龄的总和
[root@localhost ~]# awk 'NR!=1{print $2}' abc
10
20
15
18
2、模式匹配
当awk读入一行时,它试图匹配脚本中的每个模式匹配规则。只有与一个特定的模式相匹配的输入行才能成为操作对象。如果没有指定操作,与模式相匹配的输入行将被打印出来(执行打印语句是一个默认操作)。
[root@localhost ~]# cat wjj
This line of data is ingored
[root@localhost ~]# echo '' >> wjj
[root@localhost ~]# echo '' >> wjj
[root@localhost ~]# cat wjj
This line of data is ingored
[root@localhost ~]# awk '/^$/{print "This is a blank line."}' wjj //用awk对wjj进行处理,只有匹配到空行,打印This is a blank line.不是空行的就会被打印,因为没被匹配到。(意思就是有几个空行,它就会打印几句This is a blank line.)
This is a blank line.
This is a blank line.
//既打印空行又打印不是空行的内容
[root@localhost ~]# cat wjj
This line of data is ingored
[root@localhost ~]# awk 'BEGIN{print $0}/^$/{print "This is a blank line."}' wjj
//之前有内容的一行已经空了,因为之前打印了This is a blank line.,而且正文里面也没用说对这一行去做处理
This is a blank line.
This is a blank line.
测试一下脚本是整数、字符串还是空行类的
[root@localhost ~]# cat awkscr
/[0-9]+/{print "这是一个数字"}
/[a-zA-Z]+/{print "这是一个字符串"}
/^$/{print "这是一个空行"}
总的思想是,如果一个输入行能够和任何一个模式匹配,那么就执行相关的print语句。元字符+是正则表达式元字符扩展急中的一部分,他表示“一个或更多”。因此,包含一个或多个数字序列的行将被看做是一个整数。
[root@localhost ~]# awk -f awkscr
1
这是一个数字
d
这是一个字符串
这是一个空行
ctrl+d(退出)
3、程序脚本的注释
在写脚本时添加注释是一个好的习惯。注释以字符“#”开始,以换行符结束。和sed不同,awk允许在程序的任何地方添加注释。
注意:如果以命令行的方式提供awk程序,而不是将它写入一个文件中,那么在程序的任何地方都不能用单引号,否则shell将对它进行解释而导致错误.
shell脚本第一行是:#!/bin/bash
awk脚本第一行是:#!/usr/bin/awk
4、记录和字段
awk假设它的输入是有结构的,而不只是一串无规则的字符。在最简单的情况下,它将每个输入行作为一条记录,而将由空格或制表符(tab)分隔的单词作为字段(用来分隔字段的字符被称为分隔符,默认的分隔符就是空格,并且不管有多少个空格,它都会给合并成一个)。
[root@localhost ~]# cat test
John Robinson 666-555-1111
连续的两个或多个空格和/或制表符被作为一个分隔符
字段和引用的分离
awk允许使用字段操作符$来指定字段。在该操作符后面跟着一个数字或变量,用于标识字段的位置。“$1”表示第一个字段,“$2”表示第二个字段等等。“$0”表示整个输入记录。
[root@localhost ~]# awk '{print $2,$1,$3}' test
Robinson John 666-555-1111
// 用任何计算值为整数的表达式来表示一个字段,而不只是用数字和变量。用来做运算
[root@localhost ~]# echo "a b c d"|awk 'BEGIN{one=1;two=2}{print $(one+two)}'
c
// 可以在命令行中使用-F选项改变字段的分隔符。它后面跟着(或者紧跟,或者有空白)分隔符。
//用冒号做的分隔符
[root@localhost ~]# cat passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
[root@localhost ~]# awk -F':' '{print $1,$7}' passwd // ':'意思是以冒号作为分隔符; \t是表示一个实际的制表符的转义序列,它由单引号或双引号包围着
root /bin/bash
bin /sbin/nologin
daemon /sbin/nologin
[root@localhost ~]# awk -F'x:' '{print $1}' passwd // 以x:作为分隔符,x:前面的就是$1,x:后面就是$2
root:
bin:
daemon:
// 用数字做分隔符
[root@localhost ~]# awk -F'[0-9]+' '{print $1}' passwd
root:x:
bin:x:
daemon:x:
[root@localhost ~]# awk -F'[0-9]+' '{print $2}' passwd
:
:
:
//取本机的IP地址
[root@localhost ~]# ip a|grep 'inet '|grep -v '127.0.0.1'|awk -F'[ /]+' '{print $3}' // 这里是把/和空格作为分隔符
192.168.47.129
在脚本中指定域分隔符是一个好习惯并且非常方便。可以通过定义系统变量FS来改变字段风额度。因为这个必须在读取第一个输入行之前执行,所以必须在由BEGIN规则控制的操作中指定这个变量。FS默认值是空格
awk语句必须是外单内双,不然就执行不了
// 设置处理用逗号做分隔符。处理的时候是用冒号做的分隔符,输出的时候没有指定,所以它是用默认的(空格)
[root@localhost ~]# awk 'BEGIN{FS=":"}{print $1,$7}' passwd
root /bin/bash
bin /sbin/nologin
daemon /sbin/nologin
// 设置输出用-分隔符,但是$1跟$7之间必须要加上逗号,不然就不会发生改变
[root@localhost ~]# awk 'BEGIN{FS=":";OFS="-"}{print $1,$7}' passwd
root-/bin/bash
bin-/sbin/nologin
daemon-/sbin/nologin
// 字母“MA”,我们可以测试匹配指定的字段。使用(~)操作符可以测试一个字段的正则表达式,~是匹配,!~是不匹配
[root@localhost ~]# cat test
707-724-0000
(707) 724-0000
(707)724-0000
1-707-724-0000
1 707-724-0000
1(707)724-0000
[root@localhost ~]# awk '/1?[- ]?\(?[0-9]+\)?[- ]?[0-9]+-[0-9]+/' test
707-724-0000
(707) 724-0000
(707)724-0000
1-707-724-0000
1 707-724-0000
1(707)724-0000
// 写一个只匹配(707) 724-0000、(707)724-0000这两行文本的表达式
[root@localhost ~]# awk '/^\([0-9]+\) ?[0-9]+-[0-9]+/' test //^表示开头,([0-9]+都有所以不用加?,后面空格两句里面有的有有的没有所以空格后面要加?
(707) 724-0000
(707)724-0000
表达式
可以使用表达式来存储、操作和检索数据,这些操作与sed中的有很大的区别,但这是大多数程序设计语言所具有的共同特性。
一个表达式通过计算返回一个值。表达式由数字和字符串常量、变量、操作符、函数和正则表达式组成。
常量有两种类型:字符串型或数字型(“red”或1)。字符串在表达式中必须用引号括起来。
常用的转义序列:
\n 换行符
\t 水平制表符
变量是引用值的标识符。定义变量只需要为它定义一个名字并将数据赋给它即.可。变量名只能由字母、数字和下划线组成。而且不能以数字开头。变量名的大小写很重要: Salary 和salary是两个不同的变量,变量不必进行说明,你不必告诉awk什么类型的数据存储在一个变量中。每个变量有一个字符串型值和数字型值,awk能够根据表达式的前后关系来选择合适的值(不包含数字的字符串值为0)。变量不必初始化。awk 自动将它们初始化为空字符串,如果作为数字,它的值为0.
下面的表达式表示将一个值赋给x:
x=1
x是变量的名字,=是一个赋值操作符,1是一个数字常量
下面的表达式表示将字符串“hello”赋给z:
z=“hello”
空格是字符串连接操作符,表达式:
z=“hello” “world”
算术操作符
操作符 | 描述 |
---|---|
+ | 加 |
- | 减 |
* | 乘 |
/ | 除 |
% | 取模 |
^ | 取幂 |
** | 取幂 |
[root@localhost ~]# awk 'BEGIN{y=x+1;print y}' //在awk里面,那个变量在使用之前没有定义它的值得话,那么它的值就是0
1
赋值操作符
操作符 | 定义 |
---|---|
++ | 变量 加1 |
– | 变量减1 |
+= | 将加的结果赋给变量 |
-+ | 将减的结果赋给变量 |
*= | 将乘的结果赋给变量 |
/= | 将除的结果赋给变量 |
%= | 将取模的结果赋给变量 |
^= | 将取幂的结果赋给变量 |
**= | 将取幂的结果赋给变量 |
// 计算一个文件中空行的目录
[root@localhost ~]# cat test
707-724-0000
(707) 724-0000
(707)724-0000
1-707-724-0000
1 707-724-0000
1(707)724-0000
[root@localhost ~]# awk '/^$/{print x +=1}' test // 匹配第一个空行的时候加1,第2个的时候加1........以此类推
1
2
3
4
5
6
“++”是递增操作符(“-”是递减操作符)。表达式每计算一次变量的值就增加1。递增和递减操作符可以出现在操作数的任何一遍,与前缀或后缀操作符一样。位置不同可以得到不同的计算结果。
++i 在返回结果前递增x的值(前缀)
i++ 在返回结果后递增x的值(后缀)
[root@localhost ~]# awk '/^$/{print i++}' test // 先自己得到结果后再去加
0
1
2
3
4
5
[root@localhost ~]# awk '/^$/{print ++i}' test // 在得到结果之前先运算
1
2
3
4
5
6
[root@localhost ~]# awk '/^$/{++i}END{print i}' test // END一定是在正文执行完成之后再去做的
6
计算平均成绩
[root@localhost ~]# cat test
john 85 92 78 94 88
andrea 89 90 75 90 86
jasper 84 88 80 92 84
// 先求出这三个人五门成绩的总分跟平均值
[root@localhost ~]# awk '{total=$2+$3+$4+$5+$6;print total}' test
437
430
428
[root@localhost ~]# awk '{total=$2+$3+$4+$5+$6;print total/5}' test
87.4
86
85.6
// 求5门成绩的平均值
[root@localhost ~]# awk '{total=$2+$3+$4+$5+$6;avg=total/5;print $1,avg}' test
john 87.4
andrea 86
jasper 85.6
系统变量
awk中有许多系统变量或内置变量。awk有两种类型的系统变量。第一种类型定义的变量默认值可以改变,例如默认的字段和记录分隔符。第二种类型定义的变量的值可用于报告或数据处理中。例如当前记录中字段的数量,当前记录的数量等。这些可以由awk自动更新,例如,当前记录的编号和输入文件名。
有一组默认值会影响对记录和字段的输入和输出的识别。系统变量FS定义字段分隔符。它的默认值为一个空格,这将提示awk可以用若千个空格和/或制表符来分隔字段。FS可以被设置为任何单独的字符或一个正则表达式,前面,我们将分隔符改变为逗号,为的是读取-一个名字和地址的列表。和FS等效的输出是0FS,它的默认值为一个空格。
NR:行号
[root@localhost ~]# awk '{print NR".",$1}' test
1. john
2. andrea
3. jasper
商品脚本(NR)
[root@localhost ~]# cat abc
百事可乐 3
冰红茶 3
旺仔 5
农夫山泉矿泉水 2
[root@localhost ~]# cat test.sh
#!/bin/bash
echo "商品列表:"
awk '{print NR".",$1,$2"元"}' abc
read -p "请输入要买的商品编号:" choice
read -p "请输入要买的数量:" num
awk -vcount=$num -vline=$choice 'NR==line{print "您一共买了"count"瓶"$1,"共消费"$2*count"元。"}' abc
[root@localhost ~]# bash test.sh
商品列表:
1. 百事可乐 3元
2. 冰红茶 3元
3. 旺仔 5元
4. 农夫山泉矿泉水 2元
请输入要买的商品编号:3
请输入要买的数量:4
您一共买了4瓶旺仔 共消费20元。
处理多行记录
假设相同的数据保存在块格式的文件中。不是将所有的信息放置在一行,而是将任命放在一行,在下一行放置公司名、以此类推。
为了处理这种包括多行数据的记录,我们可以将字段分隔符定义为换行符,换行符用“\n”来表示,并将记录分隔符设置为空字符串,它代表一个空行。
[root@localhost ~]# awk '{print $1}' abc
百事可乐
冰红茶
旺仔
农夫山泉矿泉水
[root@localhost ~]# awk '{print $1$2}' abc //用什么东西做分隔符什么东西就会消失
百事可乐3
冰红茶3
旺仔5
农夫山泉矿泉水2
[root@localhost ~]# awk 'BEGIN{FS="\n";RS=""}{print $1}' abc // F表示字段,FS表示字段的分隔符,必须要为空,不然就乱了;R表示行,RS表示行的分隔符,行做分隔符就用\n
百事可乐 3
关系操作符和布尔操作符
操作符 | 描述 |
---|---|
< | 小于 |
> | 大于 |
<= | 小于或等于 |
>= | 大于或等于 |
== | 相等 |
!= | 不相等的 |
~ | 匹配 |
!~ | 不匹配 |
NF表示最后一列