awk命令

文本处理三剑客之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表示最后一列

上一篇:23 个非常实用的 Shell 拿来就用脚本实例


下一篇:五种开源协议的比较(BSD,Apache,GPL,LGPL,MIT) – 整理