awk命令
一、简介
先看看man手册上怎么介绍awk的: gawk - pattern scanning and processing language
可以看出awk简单来讲是具有两个功能:模式匹配和处理。这里有个疑问,man的是awk,怎么出现了gawk。awk指的是一种编程语言,用于处理文本等数据,gawk是GUN版本的awk,属于比较常用的awk版本。
二、命令执行原理
简单理解,awk是以接收到的文本的每一行为对象操作,执行指定的命令,可以理解为for语句,条件是遍历文本中的每一行,for中的代码块即是是awk后面跟随的代码块。
三、命令语法与参数
awk的基本语法:
awk 'Pattern { Action }'
Pattern,模式。awk通过模式来匹配每一行的内容,如果行内的内容和模式匹配,则提取出相应的内容,换句话说可以通过Pattern来筛选数据,之后对筛选出来的每一行数据执行action。awk里面的模式有:
1. 正则表达式
2. 关系表达式
3. 多个Pattern的逻辑组合
4. Pattern1,Pattern2
5. BEGIN 和 END
[root@redhatclient ~]# awk --help
Usage: awk [POSIX or GNU style options] -f progfile [--] file ...
Usage: awk [POSIX or GNU style options] [--] 'program' file ...
POSIX options: GNU long options:
-f progfile --file=progfile
-F fs --field-separator=fs
-v var=val --assign=var=val
-m[fr] val
-O --optimize
-W compat --compat
-W copyleft --copyleft
-W copyright --copyright
-W dump-variables[=file] --dump-variables[=file]
-W exec=file --exec=file
-W gen-po --gen-po
-W help --help
-W lint[=fatal] --lint[=fatal]
-W lint-old --lint-old
-W non-decimal-data --non-decimal-data
-W profile[=file] --profile[=file]
-W posix --posix
-W re-interval --re-interval
-W source=program-text --source=program-text
-W traditional --traditional
-W usage --usage
-W use-lc-numeric --use-lc-numeric
-W version --version
To report bugs, see node `Bugs' in `gawk.info', which is
section `Reporting Problems and Bugs' in the printed version.
gawk is a pattern scanning and processing language.
By default it reads standard input and writes standard output.
Examples:
gawk '{ sum += $1 }; END { print sum }' file
gawk -F: '{ print $1 }' /etc/passwd
四、命令实践
awk的优点在于灵活,可以通过几个简单的例子快速掌握它的原理。
1.基本功能
[root@redhatclient ~]# cat /etc/hosts
172.0.0.1 redhatclient
192.168.50.131 masterserver
192.168.50.132 redhatclient
192.168.50.133 centosmaster
[root@redhatclient ~]# cat /etc/hosts | awk '{print $0}'
172.0.0.1 redhatclient
192.168.50.131 masterserver
192.168.50.132 redhatclient
192.168.50.133 centosmaster
[root@redhatclient ~]# cat /etc/hosts | awk '{print $1}'
172.0.0.1
192.168.50.131
192.168.50.132
192.168.50.133
[root@redhatclient ~]#
在hosts文件里面有四行数据,在第二个命令中,对于接收到的每一行,awk都执行print 0这个命令, 0 这 个 命 令 , 0指一整行的数据,$1指第一列的数据,以空格为分隔符。
2.指定分隔符 -F参数
在上面,awk默认的分隔符是空格,如果不想以空格作为每行文本内的分隔符,可以使用-F参数指定分隔符。可以看到,分隔符可以用双引号括起,也可以不用。与-F参数之间可以有空格,也可以没有。
[root@redhatclient ~]# awk -F "." '{print $0}' /etc/hosts
172.0.0.1 redhatclient
192.168.50.131 masterserver
192.168.50.132 redhatclient
192.168.50.133 centosmaster
[root@redhatclient ~]# awk -F "." '{print $1}' /etc/hosts
172
192
192
192
[root@redhatclient ~]# awk -F . '{print $1}' /etc/hosts
172
192
192
192
[root@redhatclient ~]# awk -F. '{print $1}' /etc/hosts
172
192
192
192
[root@redhatclient ~]#
3.BEGIN与END
指定分隔符还可以指定内建变量FS,比较下面的输出
[root@redhatclient ~]# awk '{FS=".";print $1}' /etc/hosts
172.0.0.1
192
192
192
[root@redhatclient ~]# awk 'BEGIN{FS="."}{print $1}' /etc/hosts
172
192
192
192
[root@redhatclient ~]#
可以观察到,在第一行命令中我们没有加入BEGIN模式,结果在第一行输出没有按照我们预想的那样将此行数据按照分隔符”.”来分列。这是因为:对于读取到的每一行数据,awk先读取之后再执行花括号里的脚本,那么针对第一行数据,awk先按照默认的文本分隔符(即空格)将第一行数据分行,之后才执行花括号里面的脚本,将FS修改为”.”,所以从第二行开始才会得到我们想要的输出。
我们想要的是在awk对文本做任何处理之前就执行一些初始化代码,那么就需要使用BEGIN模式,BEGIN后面跟随的脚本会在awk处理数据之前先行执行。这也就是为什么FS一定要指定在BEGIN后的脚本中。同样的,END后的脚本会在awk处理完所有的文本之后最后执行。
4.运算符
awk可以支持以下运算符:
运算符 | 描述 |
---|---|
= += -= = /= %= ^= *= | 赋值 |
?: | C条件表达式 |
&& | 逻辑与 |
~ ~! | 匹配正则表达式和不匹配正则表达式 |
< <= > >= != == | 关系运算符 |
空格 | 连接 |
| 加,减 |
| 乘,除与求余 |
| 一元加,减和逻辑非 |
^ * | 求幂 |
++ – | 自加或者自减 |
$ | 字段引用 |
in | 数组成员 |
其中,正则和关系运算符可以用在模式里面,用于筛选数据。
[root@redhatclient ~]# cat /etc/hosts
172.0.0.1 redhatclient
192.168.50.131 masterserver
192.168.50.132 redhatclient
192.168.50.133 centosmaster
[root@redhatclient ~]# awk 'BEGIN{FS="."}$1==172{print $0}' /etc/hosts
172.0.0.1 redhatclient
其中$1==172就是awk中的模式,不用放在花括号中。
在比如,统计文本中有几行数据:
[root@redhatclient ~]# awk 'BEGIN{count=0}{count++}END{print count}' /etc/hosts
4
或者使用正则表达式来查询第一列中第二个数字是9的行:
[root@redhatclient ~]# awk 'BEGIN{FS="."}$1 ~ /.9./{print $1}' /etc/hosts
192
192
192
[root@redhatclient ~]#
正则表达式以~开始,//之间是表达式的内容
5.内建变量
awk的內建变量
变量 | 描述 |
---|---|
$n | 当前记录的第n个字段,字段间由FS分隔 |
$0 | 完整的输入记录 |
ARGC | 命令行参数的数目 |
ARGIND | 命令行中当前文件的位置(从0开始算) |
ARGV | 包含命令行参数的数组 |
CONVFMT | 数字转换格式(默认值为%.6g)ENVIRON环境变量关联数组 |
ERRNO | 最后一个系统错误的描述 |
FIELDWIDTHS | 字段宽度列表(用空格键分隔) |
FILENAME | 当前文件名 |
FNR | 各文件分别计数的行号 |
FS | 字段分隔符(默认是任何空格) |
IGNORECASE | 如果为真,则进行忽略大小写的匹配 |
NF | 一条记录的字段的数目 |
NR | 已经读出的记录数,就是行号,从1开始 |
OFMT | 数字的输出格式(默认值是%.6g) |
OFS | 输出记录分隔符(输出换行符),输出时用指定的符号代替换行符 |
ORS | 输出记录分隔符(默认值是一个换行符) |
RLENGTH | 由match函数所匹配的字符串的长度 |
RS | 记录分隔符(默认是一个换行符) |
RSTART | 由match函数所匹配的字符串的第一个位置 |
SUBSEP | 数组下标分隔符(默认值是/034) |
如打印当前文件名
[root@redhatclient ~]# awk '{print FILENAME}' /etc/hosts
/etc/hosts
/etc/hosts
/etc/hosts
/etc/hosts
6.正则表达式
awk可以支持正则表达式匹配,正则表达式又是另外一门语言,这里只简单结合awk介绍下。
awk使用正则基本语法:
awk ‘/正则/{action}’ file
上面这种默认匹配 0,相当于awk‘ 0 , 相 当 于 a w k ‘ 0 ~/正则/{action}’ file,可以指定某一列来进行正则匹配
正则匹配后默认会打印匹配到的内容,所以若主体语句仅仅是print,可直接省略。
字符 | 含义 |
---|---|
+ | 匹配一个或多个加号前的字符,这个字符可以在字符串中的任何位置。 |
* | 表示匹配零个或者多个任意字符 |
? | 匹配零个或者一个问号前的字符,这个字符可以在字符串中的任何位置。 |
| | 管道符代表逻辑或的意思,只要待匹配的字符串符合管道符两边的任何一个表达式,就代表匹配成功。 |
() | 将括号内的字符组合成一体,整体匹配。 |
{m} | 匹配前面的字符m次。 |
{m,} | 匹配前面的字符>=m次。 |
{m,n} | 匹配前面的字符次数在m和n之间,包括m和n。 |
[string] | 匹配中括号里面的任意字符,可以匹配多个。 |
[^string] | 不匹配中括号里面的任何字符。 |
\~与!\~ | \~代表指定变量进行正则表达式匹配,!\~代表指定变量不匹配。 |
^ | 指定匹配字段的开头。 |
$ | 指定匹配字段的结尾。 |
. | 表示匹配除了在末尾的换行符之外的任何一个字符。 |
\ | 反斜杠,转义字符,用在具有特殊含义的字符之前,就具有了特殊含义。\n 匹配一个换行符。\t 匹配一个制表符。\d 匹配一个数字,等价于[0-9]。\D 匹配一个非数字,等价于[^0-9]。\s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于[ \f\n\r\t\v]。[^\s]表示任何非空白字符。\S 匹配任何非空白字符。等价于[^ \f\n\r\t\v]。\w 匹配包括下划线的任何单词字符。等价于“[A-Za-z0-9_]”。\W 匹配任何非单词字符。等价于“[^A-Za-z0-9_]”。[^\w]。注意\S比\w包括@#¥等更多特殊字符。 |
\w 匹配包括下划线的任何单词字符。等价于“[A-Za-z0-9_]”。
\W 匹配任何非单词字符。等价于“[^A-Za-z0-9_]”。[^\w]
注意\S比\w包括@#¥等更多特殊字符
通过几个小例子来理解其中的含义:
[root@redhatclient test]# cat f1
t
rt
rot
root
rooot
roooot
rootrootrootroot
[root@redhatclient test]# cat f1|awk '/r*t/'
t
rt
rot
root
rooot
roooot
rootrootrootroot
[root@redhatclient test]# cat f1|awk '/r+t/'
rt
[root@redhatclient test]# cat f1|awk '/r?t/'
t
rt
rot
root
rooot
roooot
rootrootrootroot
[root@redhatclient test]# cat f1|awk '/r.t/'
rot
[root@redhatclient test]#
我们看到f1这个文件中有这这样的数据:
[root@redhatclient test]# cat f1
t
rt
rot
root
rooot
roooot
rootrootrootroot
其中
'/r*t/'
代表了匹配r大于等于零次,其中t是固定存在的,而r是待匹配的,处理第二行输出代表匹配了一次r之外,其余行均代表匹配了零次r。
'/r+t/'
代表匹配r一次或者多次,通过的,t是固定存在于r后的,只有第二行符合这个正则。
'/r?t/'
代表匹配r零次或者一次,通俗点讲,就是匹配字符串中在字符t前面有零个或者一个r的字符串。
再看:
[root@redhatclient test]# cat f1 |awk '/roo*t/'
rot
root
rooot
roooot
rootrootrootroot
[root@redhatclient test]# cat f1 |awk '/r(oo)*t/'
rt
root
roooot
rootrootrootroot
小括号代表了结合匹配。小括号里面的内容作为一个整体进行正则匹配,在第二个命令输出中,正则表达死代表的意思是,在字符串中匹配零个或以上的“oo“,并且在这些”oo“之前是字符r,之后是字符t。