案例1:插入几个新字段
在”a b c d”的b后面插入3个字段e f g
思路:在b后面插入e f g相当于在c前面插入e f g
Note:修改字段(会反馈给$0,相当于修改了$0)或者修改$0后会重建$0(根据OFS重建$0),OFS的默认值是一个空格。
案列2:格式化空白
移除每行的前缀、后缀、空白,并将各部分左对齐
原始文档格式
直接给字段赋原值就可以格式化文本,因为更改字段会以OFS重建$0
上面的方法是以单个空格隔开每个字段,但是我们想用制表符隔开每个字段的话,因该如下操作
案列3:筛选IPv4地址
从ifconfig命令的结果中筛选出除了lo网卡外的所有IPv4地址
方法1:
#ifconfig | awk '/inet / && !($2 ~ /^127/){print $2}'
方法2:
按段落读取,默认情况下awk是以行读取,所以需要修改输入分割控制符(RS)
RS="":按段落读取
RS="\0":一次性读取所有数据,但有些特殊文件中包含了空字符\0
RS="^$":真正的一次性读取所有数据,因为非空文件不可能匹配成功
RS="\n+":按行读取,但忽略所有空行
#ifconfig | awk 'BEGIN{RS=""}{print}'
只输出第一段落
#ifconfig |awk 'BEGIN{RS=""}NR==1{print}'
取得ip地址,需要知道其在第几个字段,字段默认是以空格划分的,所以数一下ip在第几个字段,注意换行符也是空格,所以ip在地6个字段
#ifconfig |awk 'BEGIN{RS=""}NR==1{print $6}'
但是以上取法不是很好,会受到网卡的排列顺序影响,进一步改进,将lo开头的网卡排除在外即可
#ifconfig |awk 'BEGIN{RS=""}!/^lo:/{print $6}'
方法3:
取出段后再按行划分字段(以换行符划分)
#ifconfig |awk 'BEGIN{RS="";FS="\n"}!/^lo:/{$0=$2;FS=" ";$0=$0;print $2}'
案例4:读取.repo配置文件中的某段
本例选取yum源的配置文件CentOS-Base.repo作为实验素材,原文如下:
# CentOS-Base.repo
#
# The mirror system uses the connecting IP address of the client and the
# update status of each mirror to pick mirrors that are updated to and
# geographically close to the client. You should use this for CentOS updates
# unless you are manually picking other mirrors.
#
# If the mirrorlist= does not work for you, as a fall back you can try the
# remarked out baseurl= line instead.
#
#
[base]
name=CentOS-$releasever - Base - mirrors.aliyun.com
failovermethod=priority
baseurl=http://mirrors.aliyun.com/centos/$releasever/os/$basearch/
http://mirrors.aliyuncs.com/centos/$releasever/os/$basearch/
http://mirrors.cloud.aliyuncs.com/centos/$releasever/os/$basearch/
gpgcheck=1
gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7
#released updates
[updates]
name=CentOS-$releasever - Updates - mirrors.aliyun.com
failovermethod=priority
baseurl=http://mirrors.aliyun.com/centos/$releasever/updates/$basearch/
http://mirrors.aliyuncs.com/centos/$releasever/updates/$basearch/
http://mirrors.cloud.aliyuncs.com/centos/$releasever/updates/$basearch/
gpgcheck=1
gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7
#additional packages that may be useful
[extras]
name=CentOS-$releasever - Extras - mirrors.aliyun.com
failovermethod=priority
baseurl=http://mirrors.aliyun.com/centos/$releasever/extras/$basearch/
http://mirrors.aliyuncs.com/centos/$releasever/extras/$basearch/
http://mirrors.cloud.aliyuncs.com/centos/$releasever/extras/$basearch/
gpgcheck=1
gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7
#additional packages that extend functionality of existing packages
[centosplus]
name=CentOS-$releasever - Plus - mirrors.aliyun.com
failovermethod=priority
baseurl=http://mirrors.aliyun.com/centos/$releasever/centosplus/$basearch/
http://mirrors.aliyuncs.com/centos/$releasever/centosplus/$basearch/
http://mirrors.cloud.aliyuncs.com/centos/$releasever/centosplus/$basearch/
gpgcheck=1
enabled=0
gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7
#contrib - packages by Centos Users
[contrib]
name=CentOS-$releasever - Contrib - mirrors.aliyun.com
failovermethod=priority
baseurl=http://mirrors.aliyun.com/centos/$releasever/contrib/$basearch/
http://mirrors.aliyuncs.com/centos/$releasever/contrib/$basearch/
http://mirrors.cloud.aliyuncs.com/centos/$releasever/contrib/$basearch/
gpgcheck=1
enabled=0
gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-7
假设现在要取出[base]段的文本,首先创建一个名为test1.awk
的文件,然后编写脚本
[root@localhost yum.repos.d]# touch test1.awk
[root@localhost yum.repos.d]# ls
CentOS-Base.repo CentOS-SCLo-scl-rh.repo test1.awk
正则中需要转义的特殊字符如下:
$ 匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 ‘\n' 或 ‘\r'。要匹配 $ 字符本身,请使用 \$
( ) 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 \( 和 \)
* 匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 \*
+ 匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 \+
. 匹配除换行符 \n之外的任何单字符。要匹配 .,请使用 \
[ ] 标记一个中括号表达式的开始。要匹配 [,请使用 \[
? 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用 \?
\ 将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如, ‘n' 匹配字符 ‘n'。'\n' 匹配换行符。序列 ‘\\' 匹配 “\”,而 ‘\(' 则匹配 “(”
^ 匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合。要匹配 ^ 字符本身,请使用 \^。
{ } 标记限定符表达式的开始。要匹配 {,请使用 \{
| 指明两项之间的一个选择。要匹配 |,请使用 \|
#/\[base\]/ 字符不确定的情况下使用这种方法
#字符确定的情况下使用index
#对于确定字符串可以使用index进行搜索,在$0中进行搜索,当其中的字串存在[base]时候,返回其索引位,索引位是大于零的,从1开始
index($0,"[base]"){
print
while( (getline var) >0 ){
if (var ~ /\[.*\]/){
exit
}
print var
}
}
#getline()的说明
#当getline()的返回值大于零时,表示已经取到数据
#当getline()的返回值等于零时,表示遇到结尾EOF,也就是没有读取到符合条件的字符
#当getline()的返回值小于零时,表示读取报错,没有权限等
#var ~ /\[.*\]/ 如果匹配到其他的就退出
案列5:根据某个字段去重
去掉 uid=xxx 重复的行
可以根据第一次出现去重,也可以根据最后一次出现去重
2019-01-13_12:00_index?uid=123
2019-01-13_13:00_index?uid=123
2019-01-13_14:00_index?uid=333
2019-01-13_15:00_index?uid=9710
2019-01-14_12:00_index?uid=123
2019-01-14_13:00_index?uid=123
2019-01-15_14:00_index?uid=333
2019-01-16_15:00_index?uid=9710
先将每一行以问号为界限划分为两个字段,然后将$2放入数组arr,统计$2出现的次数,如果只出现一次则输出,如出现多次只输出第一次出现的值
#awk -F "?" '{arr[$2]=arr[$2]+1;if(arr[$2]==1){print}}' test2.txt
也可以改为自增运算:
#awk -F "?" '{arr[$2]++;if(arr[$2]==1){print}}' test2.txt
或者
#awk -F "?" '{++arr[$2];if(arr[$2]==1){print}}' test2.txt
更进一步改进:
#awk -F "?" '!arr[$2]++{print}' test2.txt
#当第一次读取到时arr[0]返回0,取反后返回为真,则输出;第二次读取到时,返回1,取反后为假,则不输出
#而且print也可以省略,因为awk默认会输出
#awk -F "?" '!arr[$2]++' test2.txt
案列6:用awk数组做次数统计
(1)次数统计
统计的素材如下:
portmapper
portmapper
portmapper
portmapper
portmapper
portmapper
status
status
mountd
mountd
mountd
mountd
mountd
mountd
nfs
nfs
nfs_acl
nfs
nfs
nfs_acl
nlockmgr
nlockmgr
nlockmgr
nlockmgr
nlockmgr
#awk '{arr[$0]++}END{OFS="\t";for(i in arr){print arr[i],i}}' test3.txt
(2)统计TCP连接状态数量
#netstat -tnap
状态state字段是$6,将其放入数组中进行统计
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 192.168.33.99:25 0.0.0.0:* LISTEN 2388/master
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 2388/master
tcp 0 0 127.0.0.1:6010 0.0.0.0:* LISTEN 3313/sshd: root@pts
tcp 0 0 127.0.0.1:6379 0.0.0.0:* LISTEN 1114/redis-server 1
tcp 0 0 0.0.0.0:111 0.0.0.0:* LISTEN 1726/rpcbind
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1595/sshd
tcp 0 0 127.0.0.1:631 0.0.0.0:* LISTEN 2957/cupsd
tcp 0 0 0.0.0.0:52856 0.0.0.0:* LISTEN 1816/rpc.statd
tcp 0 0 192.168.33.99:22 192.168.33.102:52093 ESTABLISHED 3313/sshd: root@pts
tcp6 0 0 ::1:25 :::* LISTEN 2388/master
tcp6 0 0 ::1:6010 :::* LISTEN 3313/sshd: root@pts
tcp6 0 0 :::42719 :::* LISTEN 1816/rpc.statd
tcp6 0 0 :::111 :::* LISTEN 1726/rpcbind
tcp6 0 0 :::22 :::* LISTEN 1595/sshd
tcp6 0 0 ::1:631 :::* LISTEN 2957/cupsd
#netstat -nat|awk '/^tcp /{arr[$6]++}END{for(state in arr){print arr[state]":"state}}'
#netstat -nat | grep 'tcp' | awk '{print $6}'|sort | uniq -c
案列6:统计日志中特定条件的字段出现的次数
(1)统计日志中非200状态码的IP出现的次数
#awk '$8!=200{arr[$1]++}END{for(i in arr){print arr[i],i}}' access.log
进一步统计出现次数排名前十的IP:
#awk '$8!=200{arr[$1]++}END{for(i in arr){print arr[i],i}}' access.log | sort -k1nr | head -n 10
另外一种实现统计前十的 方法:
#awk '$8!=200{arr[$1]++}END{PROCINFO["sorted_in"]="@val_num_desc";for(i in arr){if(cnt++==10){exit}print arr[i],i}}' access.log