在"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
#法一:
[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
从ifconfig命令的结果中筛选出除了lo网卡外的所有IPv4地址。
特殊的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重新赋值,才可以打印)
[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 规则文件做筛选;读取到所需要的标签继续往下读取,直到遇到不需要的标签就暂停读取直接打印;
去掉uid=xxx重复的行。
首先利用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
[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
#法一:
[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
统计非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
需求:统计每个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 8月 5 05:06 a.com.cn.txt
-rw-r--r--. 1 root root 11 8月 5 05:06 b.com.cn.txt
-rw-r--r--. 1 root root 11 8月 5 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
当字段缺失时,直接使用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;
固定好每一行字段的宽度!
当字段中包含了字段分隔符时,直接使用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 可以收集正则匹配的结果,并将它们保存在各个字段中;
substr( ) 函数:
分割、切割函数;
该字段 在较为规整的情况下进行操作;
#法一: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
[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
就是只要第一列数字相同, 就把他们的第二列放一行上,中间空格分开;
转换得:
[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
详情查看:筛选给定时间范围内的日志
下面将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语句比较时间,如果获取时间>规定的时间就打印出来;
从而得出可以筛选出精确到秒的日志。