shell三剑客_awk

蛋蛋 2022年09月13日 70 0

awk 表示分隔符的方法

转自:https://blog.csdn.net/wzb56_earl/article/details/17511213
a). awk的选项: -F ‘分割字符’
b). awk的全局变量:FS # Field Separator
c). awk的默认的分隔符:是空白符包括: 空格、制表符、换行符

打印系统中各个用户使用的shell

# cat /etc/passwd | awk -F : '{ print $1, $7}'

或者

# cat /etc/passwd | awk 'BEGIN{ FS = ":"; } {print $1, $7}'

多字符分隔符

# echo "abcdef" | awk -F cd '{print $1, $2}'
ab ef

多种字符作为分隔符

# echo "abcdefg" | awk -F [bdf] '{print $1, $2, $3, $4}'
a c e g

特殊的多字符分隔符: ||

# echo "abc||def||hij" | gawk -F'\\|\\|' '{print #1, #2, #3}'
abc def hij
# echo "abc||def||hij" | gawk -F'\\\|\\\|' '{print #1, #2, #3}'
gawk: warning: escape sequence `\|' treated as plain `|'
abc def hij
# echo "abc||def||hij" | gawk -F'\\|\\|' '{print #1, #2, #3}'
abc def hij
# echo "abc||def||hij" | gawk -F"\\|\\|" '{print #1, #2, #3}'
gawk: warning: escape sequence `\|' treated as plain `|'
abc||def||hij
# echo "abc||def||hij" | gawk -F"\\\|\\\|" '{print #1, #2, #3}'
abc def hij
# echo "abc||def||hij" | gawk -F"\\\\|\\\\|" '{print #1, #2, #3}'
abc def hij

匹配输出

awk '/aaa/{print}' file
awk '$1 ~ /aaa/{print}' file
# $1匹配包含点"."的
awk '$1 ~ /\./{print}' file

不匹配输出

awk '!/aaa/{print}' file
awk '$1 !~ /aaa/{print}' file
# $1不匹配包含点"."的
awk '$1 !~ /\./{print}' file

设置变量

awk -v

例如:

现有如下数据:

$ cat a.txt
1 this,is,a,testfile
2 Are you ready?
3 This is a platabout linux
4 I like this.

执行如下命令,每行第一项加1:

$ awk -va=1 '{print $1,$1+a}' a.txt
1 2
2 3
3 4
4 5

也可以使用如下命令设置多个变量:

$ awk -va=1 -vb=s '{print $1,$1+a,$1b}' a.txt
1 2 1s
2 3 2s
3 4 3s
4 5 4s

通过awk脚本来运行awk命令

awk -f awk脚本 文件名

例如:

awk -f file.awk test

多个分隔符

使用-|作为分隔符

awk -F ‘[-|]’ ‘{print $3;}’ data

使用[]作为分隔符

awk -F'[][]'

输出列数

测试数据

# cat a.txt
01 02 03 04 05 06 07 08 09 10
11 12 13 14 15 16 17 18 19 20

输出前几列

# awk '{for(i = 1; i <= 5; i++) printf("%s ", $i); printf("\n")}' a.txt
01 02 03 04 05
11 12 13 14 15

输出中间几列

# awk '{for(i = 3; i <= 8; i++) printf("%s ", $i); printf("\n")}' a.txt
03 04 05 06 07 08
13 14 15 16 17 18

输出最后几列

# awk '{for(i = NF - 5 + 1; i <= NF; i++) printf("%s ", $i); printf("\n")}' a.txt  ## 最后5列
06 07 08 09 10
16 17 18 19 20

扩展

# awk '{for(i = 2; i <= 5; i++) printf("%s ", $i); {print $8, $9}}' a.txt
02 03 04 05 08 09
12 13 14 15 18 19

不输出最后一列

awk '{$NF="";print $0}' file

BEGIN和END模块

统计$1的次数,并输出大于1次的行

awk '{c[$1]++;} END {for (i in c) {if(c[i]>1) print i "\t" c[i]}}' file.txt

## 多行的写法
awk '{
    c[$1]++;
}
END {
    for (i in c) {
        if(c[i]>1)
            print i "\t" c[i]
    }
}' file.txt

例:判断输出值是否大于100,是输出2,否输出1,非数字输出0

awk 'BEGIN {found=0} {if ($1 ~ /^[0-9]+$/) {print ($1 >= 100 ? 2 : 1); found=1}} END {if (found==0) {print 0}}'

解释:使用BEGIN模块初始化一个变量found为0。然后在每行中判断$1是否为数字,如果是则输出相应的结果,并将found设为1。最后在END模块中判断found是否为0,如果是则输出0。

FS\OFS\RS\ORS

FS :是列分隔符,默认是空格
OFS:是输出列的分隔符

RS :是行分隔符,默认是空格(Record Separator)
ORS:是输出行的分隔符

即RS表示的是awk操作最小单位的边界,而FS是这个最小单位中分割的符号

![1572490942505](D:\我的文档\Dingdd for Markdown\脚本\1572490942505.png)

echo "a:b:c:d" | awk '{print $0}'
a:b:c:d
echo "a:b:c:d" | awk 'BEGIN{RS=":"}{print $0}'
a
b
c
d
echo "a:b:c:d" | awk 'BEGIN{RS=":";ORS="****"}{print $0}'
a****b****c****d

NR和FNR的区别

NR:表示当前记录数

FNR:也表示当前记录数,但是FNR的作用域只在一个文件内.如果重新打开文件,FNR会从1开始

测试文件aaa,cccc

[root@Blackghost test2]# cat aaa    //测试文件aaa  
1111:23434:zhang  
hoadsf:asdf:ccc  
[root@Blackghost test2]# cat ccc    //测试文件ccc  
1111:23434:zhang  
hoadsf:asdf:ccc  
tank:zhang:x20342  
ying:zhasdf:72342  
hosa:asdfa:2345sdf 

一个文件中NR,FNR

[root@Blackghost test2]# awk '{print NR;print FNR;print $0;}' aaa  
1     //NR  
1     //FNR  
1111:23434:zhang  
2  
2  
hoadsf:asdf:ccc 

多个文件中NR,FNR

[root@Blackghost test2]# awk '{print NR;print FNR;print $0;}' aaa ccc  
1  
1  
1111:23434:zhang  
2      //NR  
2      //FNR  
hoadsf:asdf:ccc  
3      //NR  
1      //FNR  下面的数据是来自ccc,所以NFR重置为1  
1111:23434:zhang  
4  
2  
hoadsf:asdf:ccc  
5  
3  
tank:zhang:x20342  
6  
4  
ying:zhasdf:72342  
7  
5  
hosa:asdfa:2345sdf

if

筛选tcp中接受或发送的有堆积的链接情况

netstat -tanp  | awk '{if ($2 > 0 || $3 > 0) print $0}'
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0     36 172.17.1.12:1229      11.19.14.15:8763    ESTABLISHED 29280/sshd: root@pt

if else

awk '{if ($1==1) print "A"; else if ($1==2) print "B"; else print "C"}'
grep '接口耗时:' test.log|cut -d "m" -f 1 |cut -d ":" -f 2|awk 'BEGIN {count=ms1=ms2=ms3=0}{if($1<1) ms1++;else if($1>=1&&$1<=5) ms2++;else ms3++;count++}END{printf ("ms1,ms1占比 %s,%.2f%%\n",ms1,ms1/count*100);printf ("ms2,ms2占比 %s,%.2f%%\n",ms2,ms2/count*100);printf ("ms3,ms3占比 %s,%.2f",ms3,ms3/count*100,"%")}'
ms1,ms1占比 0 , 0.00 %
ms2,ms2占比 17 , 89.47 %
ms3,ms3占比 2 , 10.53 %

匹配特殊字符

.-->还没找到办法

abb="abc_78[edd0.2]"
awk '{print $0;if($0~'"$abb"'){print "no";exit}}' file
awk '{print $0;if($0~/abc_78\[edd0.2\]/){print "no";exit}}' file

# 下列命令还未实现
abb="."
tac dishttp.INFO| grep REQUEST | grep false | awk '{print $10}' | grep ? | awk -F'?' '{print $1}' | awk -v akey=$abb '{if($NF~'"$abb"'){print $0}}' | more

函数

substr()截取字段

# echo "123456789 3333344" | awk '{print substr($1,5,2)}'
56

$1指的是第一列,也就是123456789
5指的是从第5个字符开始
2指的是截取2个字符

# echo "12345678dsdsd" | cut -c1-10
12345678ds

以字符为单位进行割接

# a=123456789;echo ${a:4:2}
56

这个是从0开始算,所以是4:2

getline函数

得到行,获取当前行的下一行。

getline命令是我个人认为awk最强大的一个命令。因为它彻底改变了awk的运行逻辑。awk本质上就是一个for循环,它每次对输入文件的一行进行处理,然后转而执行下一行,直到整个文件的每一行都被执行完毕。整个过程是自动的,你无需做什么。但是,getline命令却可以让你去控制循环。当然,getline命令执行后,awk会设置NF,NR,FNR和$0等这些内部变量。

打印出从1到10之间的偶数

$ seq 10 | awk '{getline; print $0}'
2
4
6
8

awk首先读取到了第一行,就是1,然后getline,就得到了1下面的第二行,就是2,因为getline之后,awk会改变对应的NF,NR,FNR和$0等内部变量,所以此时的$0的值就不再是1,而是2了,然后将它打印出来。以此类推,就可以得到上面的结果。

只打印出奇数行

$ seq 10 | awk '{print $0; getline}'
1
3
5
7

奇偶行对调打印

$ seq 10 | awk '{getline tmp; print tmp; print $0}'
2
1
4
3
6
5
8
7
10
9

将getline得到的下一行的内容放在了tmp这个变量里,因此NF,NR,FNR和$0等内部变量并不会被改变。

从另外一个文件中读取内容

$ awk '{printf "%s ", $0; getline < "b.txt"; print $0}' a.txt 
1 6
2 7
3 8
4 9
5 20

通过getline得到系统的当前时间

$ awk 'BEGIN {"date" | getline; close("date"); print $0}'
Tue May 10 07:50:51 PDT 2016

split

awk的内建函数split允许你把一个字符串分隔为单词并存储在数组中。你可以自己定义域分隔符或者使用现在FS(域分隔符)的值。1在awk中是多维数组的时候,若需要访问数组中某一元素内部的字符,必须使用split(item,subscr,SUBSEP)函数来访问单独的下标分量

split(),对于从字段中提取“子字段”有很大用处,split(string,aray,separator),string 是要被分解到名为array的元素的输入字符串,数组的下标从1开始到n,n即为数组中元素的个数,元素根据separator分隔符来分解,separator可以支持一个完整的正则表达式。默认为FS。

格式:

  split (string, array, field separator)
  split (string, array) -->如果第三个参数没有提供,awk就默认使用当前FS值。

例子:

例:替换分隔符

time="12:34:56"
out=`echo $time | awk '{split($0,a,":");print a[1],a[2],a[3]}'`
echo $out

例:计算指定范围内的和(计算每个人1月份的工资之和)

[root@test ~]# cat test.txt
Tom    2012-12-11      car     53000
John   2013-01-13      bike    41000
vivi    2013-01-18      car     42800
Tom    2013-01-20      car     32500
John   2013-01-28      bike    63500
[root@test ~]# awk '{split($2,a,"-");if(a[2]==01){b[$1]+=$4}}END{for(i in b)print i,b[i]}' test.txt
vivi 2800
Tom2500
John4500

例:利用split做格式转换

1.1.1.1	3	2024-03-12_04:59:00|2024-03-12_10:38:00|2024-03-12_21:23:00

shell中,如何将上面的写法写成下面的样子

1.1.1.1	3	2024-03-12_04:59:00	2024-03-12
1.1.1.1	3	2024-03-12_10:38:00	2024-03-12
1.1.1.1	3	2024-03-12_21:23:00	2024-03-12
# echo "1.1.1.1    3    2024-03-12_04:59:00|2024-03-12_10:38:00|2024-03-12_21:23:00" | awk -F'\t' '{split($3, a, "|"); for (i in a) { split(a[i], b, "_"); print $1 "\t" $2 "\t" a[i] "\t" b[1]}}'
1.1.1.1	3	2024-03-12_04:59:00	2024-03-12
1.1.1.1	3	2024-03-12_10:38:00	2024-03-12
1.1.1.1	3	2024-03-12_21:23:00	2024-03-12

参考:
https://blog.csdn.net/lovedingd/article/details/121698445

Last Updated: 2024/03/27 22:05:39
shell_数组 抓包工具_Wireshark常用过滤规则