AWK---经典实例

文章目录

  • 1、插入几个新字段:
  • 2、格式化空白:
  • 3、筛选IPV4地址:
  • 4、读取.ini配置文件中的某段:
  • 5、根据某字段去重:
  • 6、次数统计:
  • 7、统计TCP连接状态数量:
  • 8、统计日志中各IP访问非200状态码的次数:
    • 日志实例:
  • 9、统计独立IP:
  • 10、处理字段缺失的数据:
  • 11、处理字段中包含了字段分隔符的数据:
  • 12、取字段中指定字符数量:
  • 13、行列转换:
  • 14、行列转换2:
  • 15、筛选给定时间范围内的日志:

1、插入几个新字段:

在"a b c d"的 b 后面插入3个字段 e f g。

[root@localhost ~]# echo "a b c d" | awk '{$2=$2" e f g ";print}'
a b e f g  c d

2、格式化空白:

移除每行的前缀、后缀空白,并将各部分左对齐
image.png

#法一:
[root@localhost ~]# awk '{$1=$1;print}' 2.awk 
aaa bbb ccc
bbb aaa ccc
ddd fff eee ggg hh jj

#法二:
[root@localhost ~]# awk 'BEGIN{OFS="\t"}{$1=$1;print}' 2.awk 
aaa	bbb	ccc
bbb	aaa	ccc
ddd	fff	eee	ggg	hh	jj

3、筛选IPV4地址:

从ifconfig命令的结果中筛选出除了lo网卡外的所有IPv4地址。

AWK---经典实例_第1张图片

特殊的RS值用来解决特殊读取需求:

  • RS="”: 按段落读取;
  • RS="\0”: 一次性读取所有数据,但有些特殊文件中包含了空字符 \00RS="~s”:真正的一次性读取所有数据,因为非空文件不可能匹配成功;
  • RS="n+”: 按行读取,但忽略所有空行;

FS为输入分割符;

#法一:(按照行取值)
[root@localhost ~]# ifconfig | awk '/inet /&& !($2 ~ /^127/){print $2}'
192.168.40.141
192.168.122.1

#法二:(按照段落取值)
[root@localhost ~]# ifconfig | awk 'BEGIN{RS=""}!/lo/{print $6}'
192.168.40.141
192.168.122.1

#法三:(划分每行找第二个元素)
[root@localhost ~]# ifconfig | awk 'BEGIN{RS="";FS="\n"}!/lo/{$0=$2;FS=" ";$0=$0;print $2}'
192.168.40.141
( 改变分隔符之后必须用$0=$0重新赋值,才可以打印)

4、读取.ini配置文件中的某段:

AWK---经典实例_第2张图片

[root@localhost ~]# cat 1.awk 
BWGIN{RS=""}   #按段落
/\[mysql\]/{
	print;
	while((getline)>0){
		if(/\[.*\]/){
			exit
		}
		print
	}
}

[root@localhost ~]# awk -f 1.awk dem1.txt 
[mysql]
name=mysql_repo
baseurl=https://xxx/mysql-repo/yum/mysql-5.7-community/el/$releasever/$basearch

gpgcheck=0
enable=1

上代码,利用 -f 选项 利用 awk 规则文件做筛选;读取到所需要的标签继续往下读取,直到遇到不需要的标签就暂停读取直接打印;

5、根据某字段去重:

去掉uid=xxx重复的行。

AWK---经典实例_第3张图片

首先利用uid去重,我们需要利用 ? 进行划分,然后将uid=xxx保存在数组中,这是判断重复的依据;
然后统计uid出现次数,第一次出现统计,第二次不统计;

[root@localhost ~]# awk -F"?" '!arr[$2]++{print}'  dem3.txt 
2019-01-13_12:00_index?uid=123
2019-01-13_14:00_index?uid=333
2019-01-13_15:00_index?uid=9710

[root@localhost ~]# awk -F"?" '{arr[$2]=arr[$2]+1;if(arr[$2]==1){print}}' dem3.txt 
2019-01-13_12:00_index?uid=123
2019-01-13_14:00_index?uid=333
2019-01-13_15:00_index?uid=9710

[root@localhost ~]# awk -F"?" '{arr[$2]++;if(arr[$2]==1){print}}' dem3.txt 
2019-01-13_12:00_index?uid=123
2019-01-13_14:00_index?uid=333
2019-01-13_15:00_index?uid=9710

6、次数统计:

AWK---经典实例_第4张图片

[root@localhost ~]# awk '{arr[$0]++}END{for(i in arr){print arr[i],i}}' dem2.txt 
4 nfs
2 status
5 nlockmgr
6 portmapper
2 nfs_acl
6 mountd

7、统计TCP连接状态数量:

AWK---经典实例_第5张图片

#法一:
[root@localhost ~]# netstat -antp | awk '{arr[$6]++}END{for(i in arr){print arr[i],i}}'
11 LISTEN
1 CLOSE_WAIT
1 ESTABLISHED
1 established)
1 Foreign

注意:该命令会出现不必要的信息统计产生干扰;

#法二:
[root@localhost ~]# netstat -antp | awk '/^tcp/{arr[$6]++}END{for(i in arr){print arr[i],i}}'
11 LISTEN
1 CLOSE_WAIT
1 ESTABLISHED
  
#法三:( 具体到头部名称 )
root@localhost ~]# netstat -antp | awk '/^tcp/{arr[$6]++}END{for(state in arr){print arr[state] ":" state}}'
11:LISTEN
1:CLOSE_WAIT
1:ESTABLISHED

8、统计日志中各IP访问非200状态码的次数:

日志实例:

AWK---经典实例_第6张图片

统计非200状态码的IP,并取次数最多的前10个IP。

awk 中排序函数 sort asort;
设置 awk 排序顺序 PROCINFO;
PROCINFO[“sorted_in”]=@val_num_desc

#法一:
[root@localhost ~]# awk '$9!=200{arr[$1]++}END{for(i in arr){print arr[i],i}}' access.log | sort -nr | head -n 10
28 127.0.0.1
3 192.168.1.5
2 192.168.1.1
1
  
/注意:该命令弊端是 无时间限制规定


#法二:
[root@localhost ~]# cat 4.awk 
$9!=200{arr[$1]++}
END{
	PROCINFO["sorted_in"]="@val_num_desc";
	for(i in arr){
		#设置计时器
		if(cnt++==10){exit}
		print arr[i],i
	}
}

[root@localhost ~]# awk -f 4.awk access.log 
28 127.0.0.1
3 192.168.1.5
2 192.168.1.1
1 

9、统计独立IP:

? url 访问IP 访问时间 访问人
AWK---经典实例_第7张图片

需求:统计每个URL的独立访问IP有多少个(去重),并且要为每个URL保存一个对应的文件,得到的结果:

[root@localhost ~]# cat 5.awk 
BEGIN{
	FS="|"
}
#类似去重
!arr[$1,$2]++{
	arr1[$1]++  #计数统计
}
END{
   for(i in arr1){
	   print i,arr1[i] > (i".txt")
   }
}

[root@localhost ~]# awk -f 5.awk dem4.txt
[root@localhost ~]# ll | grep com.cn
-rw-r--r--. 1 root root    11 85 05:06 a.com.cn.txt
-rw-r--r--. 1 root root    11 85 05:06 b.com.cn.txt
-rw-r--r--. 1 root root    11 85 05:06 c.com.cn.txt

[root@localhost ~]# cat a.com.cn.txt b.com.cn.txt c.com.cn.txt 
a.com.cn 2
b.com.cn 2
c.com.cn 1


10、处理字段缺失的数据:

AWK---经典实例_第8张图片

当字段缺失时,直接使用FS划分字段来处理会非常棘手。gawk为了解决这种特殊需求,提供了FIELDWIDTHS变量

FIELDWIDTH 可以按照字符数量划分字段。

需求:将空白部分保留下来;

[root@localhost ~]# awk '{print $6}' FIELDWIDTHS="2 2:6 2:6 2:3 2:13 2:11" dem5.txt
phone
18023394012
18084925203
17048792503
17023929033
18185904230
18923902352
18785234906
17729348758
15947893212
13942943905

[root@localhost ~]# awk '{print $5,$6}' FIELDWIDTHS="2 2:6 2:6 2:3 2:13 2:11" dem5.txt
email         phone
abc@qq.com    18023394012
def@gmail.com 18084925203
              17048792503
bbb@189.com   17023929033
ccc@xyz.com   18185904230
ddd@139.com   18923902352
exdsa@189.com 18785234906
bax@qq.com    17729348758
bc@sohu.com   15947893212
bcbd@139.com  13942943905

上代码:
FIELDWIDTH第一个字段是字符宽度ID为2,指定2个字符宽度第两个字段最大为6,但前面和ID之间还有两个空格,所以可以指定宽度为8,也可以跳过两个字符2:6;
固定好每一行字段的宽度!

11、处理字段中包含了字段分隔符的数据:

下面是CSV文件中的一行,该CSV文件以逗号分隔各个字段。
image.png

当字段中包含了字段分隔符时,直接使用FS划分字段来处理会非常棘手。gawk为了解决这种特殊需求,提供了FPAT变量

**FPAT **可以收集正则匹配的结果,并将它们保存在各个字段中。(就像grep匹配成功的部分会加颜色显示,而使用FPAT划分字段,则是将匹配成功的部分保存在字段$1 $2 $3…中)。

需求:取得第三个字段"1234 A Pretty Street, NE"

[root@localhost ~]# cat dem6.txt | awk 'BEGIN{FPAT="[^,]+|\".*\""}{print $3}'
"1234 A Pretty Street, NE"

[^, ] —除了逗号以外不匹配,说明 逗号 不为分隔符;
$1—Robbins
$2—Arnold
$3—“1234 A Pretty Street, NE”
$4—MyTown
$5—MyState
FPAT 可以收集正则匹配的结果,并将它们保存在各个字段中;

12、取字段中指定字符数量:

substr( ) 函数:

分割、切割函数;

AWK---经典实例_第9张图片

该字段 在较为规整的情况下进行操作;

#法一:awk字符索引从1开始
[root@localhost ~]# awk '{print $1,substr($2,1,4)}' dem7.txt 
16 001a
16 002a
23 001a
23 001f

#法二:利用 FIELDWIDTH #分割
[root@localhost ~]# awk 'BEGIN{FIELDWIDTHS="2 2:4"}{print $1,$2}' dem7.txt 
16 001a
16 002a
23 001a
23 001f

13、行列转换:

image.png
转换得:

[root@localhost ~]# awk '{for(i=1;i<=NF;i++){arr[i]=arr[i]" "$i}}END{for(i=1;i<=NF;i++){print arr[i]}}' dem8.txt 
 name alice ryan
 age 21 30

14、行列转换2:

AWK---经典实例_第10张图片

就是只要第一列数字相同, 就把他们的第二列放一行上,中间空格分开;

转换得:

[root@localhost ~]# awk '{if($1 in arr){arr[$1]=arr[$1]" "$2}else{arr[$1]=$2}}END{for(i in arr){printf "%s %s\n",i,arr[i]}}' dem9.txt 
74683 1001 1002 1011
74684 1000 1001 1002
74685 1001 1011
74686 1000
100085 1000 1001

15、筛选给定时间范围内的日志:

详情查看:筛选给定时间范围内的日志

下面将strptime2()实现的是将
**27/Jul/2023:18:36:30+0800 **格式的字符串转换成 epoch 值,然后和 which_time 比较大小,既可以筛选出精确到秒的日志。

BEGIN{
  # 要筛选什么时间的日志,将其时间构建成epoch值
  which_time = mktime("2023 07 27 18 36 30")
}

{
  # 取出日志中的日期时间字符串部分
  match($0,"^.*\\[(.*)\\].*",arr)

  # 将日期时间字符串转换为epoch值
  tmp_time = strptime2(arr[1])

  # 通过比较epoch值来比较时间大小
  if(tmp_time > which_time){
    print
  }
}

# 构建的时间字符串格式为:"27/Jul/2023:18:36:30+0800"
function strptime2(str,dt_str,arr,Y,M,D,H,m,S) {
  dt_str = gensub("[/:+]"," ","g",str)
  # dt_sr = "27 Jul 2023 18 36 30 08 00"
  split(dt_str,arr," ")
  Y=arr[3]
  M=mon_map(arr[2])
  D=arr[1]
  H=arr[4]
  m=arr[5]
  S=arr[6]
  return mktime(sprintf("%s %s %s %s %s %s",Y,M,D,H,m,S))
} 
function mon_map(str,mons){
  mons["Jan"]=01
  mons["Feb"]=02
  mons["Mar"]=03
  mons["Apr"]=04
  mons["May"]=05
  mons["Jun"]=06
  mons["Jul"]=07
  mons["Aug"]=08
  mons["Sep"]=09
  mons["Oct"]=10
  mons["Nov"]=11
  mons["Dec"]=12
  return mons[str]
}

上代码:(理解过程)
首先通过 mktime函数将日期时间信息转换为 epoch 值时间戳,这样就可以直接比较时间了;
其次使用 match函数匹配正则表达式,取出日志中的日期时间字符串部分;
然后通过 strptime2()函数转为 epoch值。函数内部通过 gensub函数匹配正则,将 “/ : +”替换成空格,这样 mktime函数就可以转换了,(同时也可以使用 split函数,该函数也是将字符串按照空格分隔),然后保存在 arr数组,而后给 YMDHmS赋值,mon_map函数,将月份的英文转换为数字,最后返回 mktime函数处理后的 epoch值;
最后 if语句比较时间,如果获取时间>规定的时间就打印出来;
从而得出可以筛选出精确到秒的日志。



感谢点赞支持,小Rong会继续努力!!!

你可能感兴趣的:(运维)