文本三剑客之awk

1. awk简介

awk 是一种用于处理文本的编程语言工具,是一个报告生成器,拥有强大的文本格式化的能力;同时它支持条件判断、数组、循环等功能,所以,我们也可以把awk理解成一个脚本语言解释器。

awk的创始人是Alfred Aho 、Peter Weinberger 和 Brian Kernighan,它的名字由三个人的姓氏的首字母组成。

awk经过改进生成的新的版本nawk,gawk,现在默认linux系统下日常使用的是gawk,用命令可以查看正在应用的awk的来源(ls -l /bin/awk )。

文本三剑客的特长:

grep 适合单纯的查找或匹配文本

sed 适合编辑匹配到的文本

awk 适合格式化文本,对文本进行较复杂格式处理

awk语法

awk [options] 'Pattern{Action}' file
其中 Pattern{Action} 是使用awk要用的命令(program),要用引号

2. 常用动作之print

awk是逐行处理的.意思就是说,当awk处理一个文本时,会一行一行进行处理,处理完当前行,再处理下一行,awk默认以"换行符"为标记,识别每一行。awk会按照用户指定的分割符去分割当前行,如果没有指定分割符,默认使用空格作为分隔符。
在这里插入图片描述
$0 表示显示整行 ,$NF表示当前行分割后的最后一列(它们均为内置变量)
注意,$NFNF 要表达的意思是不一样的,对于awk来说,$NF表示最后一个字段,NF表示当前行被分隔符切开以后,一共有几个字段。
也就是说,假如一行文本被空格分成了7段,那么NF的值就是7,$NF的值就是$7, 而$7表示当前行的第7个字段,也就是最后一列,那么每行的倒数第二列可以写为$(NF-1)

awk擅长文本格式化,并且将格式化以后的文本输出,所以awk最常用的动作就是print和printf。

//不使用[options] ,也不指定pattern,使用最简单的action,示例:
[root@master ~]# cat test 
123 abc 789 lll
8888 000 222 jkjl 4343
666 999 212
//打印以上文本内容
[root@master ~]# awk '{print}' test 
123 abc 789 lll
8888 000 222 jkjl 4343
666 999 212
[root@master ~]# awk '{print $0}' test 
123 abc 789 lll
8888 000 222 jkjl 4343
666 999 212

//打印第二列的内容
[root@master ~]# awk '{print $2}' test 
abc
000
999
//$2表示将当前行按照分隔符分割后的第2列,不指定分隔符时,默认使用空格作为分隔符。

//表示当前行被分割后的最后一列
[root@master ~]# awk '{print $NF}' test 
lll
4343
212
//倒数第二列
[root@master ~]# awk '{print $(NF-1)}' test 
789
jkjl
999

//打印多列,用逗号隔开
[root@master ~]# awk '{print $1,$2}' test 
123 abc
8888 000
666 999

除了输出文本中的列,我们还能够添加字段,将添加的字段与文件中的列结合起来。可以实现自由组合。
特殊字符要用引号,内置变量不能使用引号。

[root@master ~]# awk '{print $1,$2,"xixi"}' test 
123 abc xixi
8888 000 xixi
666 999 xixi
[root@master ~]# awk '{print $1,$2,999}' test 
123 abc 999
8888 000 999
666 999 999
[root@master ~]# awk '{print "第一列:"$1,"第二列:"$2}' test 
第一列:123 第二列:abc
第一列:8888 第二列:000
第一列:666 第二列:999

3. 特殊模式 BEGIN 和 END

AWK 包含两种特殊的模式:BEGIN 和 END。

  • BEGIN 模式指定了处理文本之前需要执行的操作
  • END 模式指定了处理完所有行之后所需要执行的操作
[root@master ~]# awk 'BEGIN{print "zzz","222"} {print $1}' test 
zzz 222
123
8888
666
//意思是说先打印zzz和222然后在打印$1

[root@master ~]# awk '{print $1}END{print "zzz","222"}' test 
123
8888
666
zzz 222
//意思是说先打印$1然后在打印zzz和222

//组合使用
[root@master ~]# awk 'BEGIN{print "zzz","222"}{print $1}END{print "333","444"}' test 
zzz 222
123
8888
666
333 444

4. 分隔符

4.1 输入分隔符

  • 输入分隔符(field separator),简称FS
  • 输入分割符,awk默认以空格为分隔符对每一行进行分割
  • 当一个文本中没有空格的时候,可以指定以特殊的字符或者符号作为输入分隔符对文本进行分割
  • 特殊字符如\ > < |等作为输入分隔时必须加引号
[root@minion2 ~]# cat test 
123:1p1:bbb
ccc:8p8:999:0000

//指定以:为输入分隔符对文本进行分割
[root@minion2 ~]# awk -F ':' '{print $1,$2}' test 
123 1p1
ccc 8p8
//或者不用引号直接紧跟-F后面
[root@minion2 ~]# awk -F: '{print $1,$2}' test 
123 1p1
ccc 8p8

//指定以p为输入分隔符对文本进行分割
[root@minion2 ~]# awk -F 'p' '{print $1,$2}' test 
123:1 1:bbb
ccc:8 8:999:0000

//特殊字符必须加引号
[root@minion2 ~]# cat test 
123>1p1>bbb
ccc>8p8>999>0000
[root@minion2 ~]# awk -F '>' '{print $1,$2}' test 
123 1p1
ccc 8p8

同时我们还可以使用-v选项来指定变量,awk内置变量FS可以用于指定输入分隔符

[root@minion2 ~]# cat test 
123>1p1>bbb
ccc>8p8>999>0000
[root@minion2 ~]# awk -v FS='>' '{print $1,$2}' test 
123 1p1
ccc 8p8

4.2 输出分隔符

  • 输出分隔符默认也是空格
  • awk输出每一列的时候,会使用空格隔开每一列。这个空格,就是awk的默认的输出分隔符。

在这里插入图片描述
通过-v选项来设置内部变量OFS指定输出分隔符

[root@minion2 ~]# cat test 
123>1p1>bbb
ccc>8p8>999>0000

[root@minion2 ~]# awk -v FS='>' -v OFS='#' '{print $1,$2}' test 
123#1p1
ccc#8p8

5. 变量

变量名释义
FS输入分隔符,默认为空格
OFS输出分隔符,默认为空格
RS输入行记录分隔符,默认是回车换行符
ORS输出行记录分隔符,默认是回车换行符
NF当前行的字段个数,即被分割成了几列
NR当前文本的行号,即有几行
FNR处理多个文本时,分别记录每个文本的行号
FILENAME当前文件名
ARGC命令行参数的个数
ARGV数组,保存的是命令行所给定的各参数
$0当前记录
$1 - $n当前记录的第n行
$NF当前记录的最后一列
$(NF-n)当前记录的倒数NF-n行

示例:

5.1 NR

//查看当前文本有几行
[root@localhost ~]# cat test 
123>1p1>bbb
ccc>8p8>999>0000
[root@localhost ~]# awk '{print NR}' test 
1
2

//NR和NF结合使用
[root@localhost ~]# awk '{print NR,NF}' test 
1 1
2 1
//惊奇的发现为啥第一行和第二行都只有一列,因为前文已经说过默认的输入分隔符是空格,这里你没有指定输入分隔符,所以它会把每一行当做一个整体

//我们来指定>号为输入分隔符,看有什么变化
[root@localhost ~]# awk -F'>' '{print NR,NF}' test 
1 3
2 4
//指定>号为分隔符以后,第一行被分割成了3列,第二行被分隔成了4列

//为整个文本添加行号
[root@localhost ~]# awk '{print NR,$0}' test 
1 123>1p1>bbb
2 ccc>8p8>999>0000

//使用NR取出IP地址
[root@localhost ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:0c:29:18:5b:1c brd ff:ff:ff:ff:ff:ff
    inet 192.168.152.131/24 brd 192.168.152.255 scope global dynamic noprefixroute ens33
       valid_lft 1597sec preferred_lft 1597sec
    inet6 fe80::2a84:f1c4:6a4:1159/64 scope link noprefixroute
       valid_lft forever preferred_lft forever
[root@localhost ~]# ip a|awk 'NR==9 {print $2}'|awk -F/ '{print $1}'
192.168.152.131

5.2 FNR

[root@localhost ~]# cat test
123>1p1>bbb
ccc>8p8>999>0000
[root@localhost ~]# cat test1
xixi hehe haha 111
222 888 777 000 9999

[root@localhost ~]# awk '{print FNR,$0}' test test1
1 123>1p1>bbb
2 ccc>8p8>999>0000
1 xixi hehe haha 111
2 222 888 777 000 9999
//上面两个文本的行号分别单独记录

5.3 RS

上面说到RS是输入行分隔符,默认的换行分隔符就是我们平常用的回车换行。

[root@localhost ~]# cat test
123>1p1>bbb
ccc>8p8>999>0000

[root@localhost ~]# awk -v RS='>' '{print NR,$0}' test
1 123
2 1p1
3 bbb
ccc
4 8p8
5 999
6 0000
//这里我们指定>为输入行分隔符,
可以看到它每遇到>就换行,一共有6行。
bbb和ccc被记录成了一行,这是因为它不在以我们熟知的回车换行,
必须要以我们指定的>才会换行

5.4 ORS

上面说到ORS是输出行分隔符,默认也是回车换行

[root@localhost ~]# cat test1
xixi hehe haha 111
222 888 777 000 9999

[root@localhost ~]# awk -v ORS='///' '{print NR,$0}' test1
1 xixi hehe haha 111///2 222 888 777 000 9999///[root@localhost ~]#
我们指定///为输出行分隔符
那么文本将以///换行
你可能会有疑问,它并没有换行啊?
那是因为在你惯性的思维里还是觉得回车才是换行
换行,就是另起一行
而这里所谓的换行就是输出///

//RS和ORS结合使用
[root@localhost ~]# awk -v RS=' ' -v ORS='///' '{print NR,$0}' test1
1 xixi///2 hehe///3 haha///4 111
222///5 888///6 777///7 000///8 9999
如果能理解这个示例的意思,那么一定就学会了使用RS和ORS的用法

5.4 FILENAME

显示当前文件名,这个比较简单,容易理解

[root@localhost ~]# cat test1
xixi hehe haha 111
222 888 777 000 9999

[root@localhost ~]# awk '{print FILENAME,NR,$0}' test1
test1 1 xixi hehe haha 111
test1 2 222 888 777 000 9999

5.5 ARGC和ARGV

  • ARGV表示一个数组,命令行直接给,数组的索引都是从0开始的,0是awk本身
  • ARGC记录这个数组的个数
[root@localhost ~]# awk 'BEGIN{print "aaa",ARGV[0],ARGV[1],ARGV[2],ARGC}' 11 22
aaa awk 11 22 3
//先输出aaa然后输出ARGV[0],ARGV[1],ARGV[2]
//一共有3个参数

6、printf

看一下printf的输出动作,可以看到 printf 不会输出换行符,默认会将文本输出在一行里面。

[root@localhost ~]# cat test
abc 12345 gg
cde 897 ll vv

[root@localhost ~]# awk '{printf $0}' test
abc 12345 ggcde 897 ll vv[root@localhost ~]# 

printf 通过格式替换符可以将文本格式化输出,常见的格式替换符如下:

格式替换符释义
%s字符串
%b相对应的参数中包含转义字符时,可以使用此替换符进行替换,对应的转义字符会被转义
%cASCII字符。显示相对应参数的第一个字符
%d, %i十进制整数
%o不带正负号的八进制值
%u不带正负号的十进制值
%x不带正负号的十六进制值,使用a至f表示10至15
%X不带正负号的十六进制值,使用A至F表示10至15
%x%%

转义字符如下:

转义符释义
\a警告字符,通常为ASCII的BEL字符
\b后退
\c抑制(不显示)输出结果中任何结尾的换行字符(只在%b格式指示符控制下的参数字符串中有效),而且,任何留在参数里的字符、任何接下来的参数以及任何留在格式字符串中的字符,都被忽略
\f换页(formfeed)
\n换行
\r回车(Carriage return)
\t水平制表符
\v垂直制表符
\一个字面上的反斜杠字符,即”\”本身。
\ddd表示1到3位数八进制值的字符,仅在格式字符串中有效
\0ddd表示1到3位的八进制值字符

在使用 printf 时,指定的格式和打印输出的内容之间要用逗号隔开,而且一个格式对应一个要格式化输出的内容

# 可以看到,虽然你指定了要 $2 ,但是 $2 的内容并没有输出,要想输出,你必须再指定一个格式,格式和输出的内容一一对应
[root@localhost ~]# awk '{printf "%s\n",$1}' test
abc
cde
[root@localhost ~]# awk '{printf "%s\n",$1,$2}' test
abc
cde
[root@localhost ~]# awk '{printf "%s %s\n",$1,$2}' test
abc 12345
cde 897
使用 awk 格式化输出 passwd 文档
[root@localhost ~]# awk -F ":" '{printf "%s %-16s %s %-5s %s %-5s\n","用户名:",$1,"UID:",$3,"GID:",$4}' /etc/passwd
用户名: root             UID: 0     GID: 0
用户名: bin              UID: 1     GID: 1
用户名: daemon           UID: 2     GID: 2
用户名: adm              UID: 3     GID: 4
用户名: lp               UID: 4     GID: 7
用户名: sync             UID: 5     GID: 0
用户名: shutdown         UID: 6     GID: 0
用户名: halt             UID: 7     GID: 0
用户名: mail             UID: 8     GID: 12
用户名: operator         UID: 11    GID: 0
用户名: games            UID: 12    GID: 100
用户名: ftp              UID: 14    GID: 50
用户名: nobody           UID: 65534 GID: 65534
用户名: dbus             UID: 81    GID: 81
用户名: systemd-coredump UID: 999   GID: 997
用户名: systemd-resolve  UID: 193   GID: 193
用户名: tss              UID: 59    GID: 59
用户名: polkitd          UID: 998   GID: 996
用户名: unbound          UID: 997   GID: 994
用户名: sssd             UID: 996   GID: 993
用户名: sshd             UID: 74    GID: 74
用户名: rngd             UID: 995   GID: 992
用户名: nginx            UID: 994   GID: 991

7、模式(pattern)

  • 关系运算模式
  • 空模式
  • BEGIN/END模式
  • 正则模式
  • 行范围模式

模式可以理解为条件,awk 是逐行处理文本的,如果我们指定了条件,只有满足 条件 的行才会被处理,不满足”条件”的行就不会被处理。

[root@localhost ~]# cat test
abc 12345 gg
cde 897 ll vv
rng 90 44 yyds xn
[root@localhost ~]# awk 'NF==5{print $0}' test
rng 90 44 yyds xn
[root@localhost ~]# awk 'NR==1{print $0}' test
abc 12345 gg

7.2 关系运算模式

可以看出上面的示例中,模式用到了关系运算符,我们称之为关系运算模式,awk 支持的关系运算符如下图:
在这里插入图片描述

7.3 正则模式

awk ‘/正则表达式/{Action}’
扩展正则加 --posix 选项或者 --re-interval 选项

特殊字符如果代表本身需要转义
[root@localhost ~]# awk '/\/bin\/bash$/{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash

用awk取出如下文件最少有两个a,最多只有三个a的行
[root@localhost ~]# cat txt
hauyi
haaiuik
haaahkh
haaaauyy
haaaaajklkl
[root@localhost ~]# awk --posix '/ha{2,3}[^a]/{print $0}' txt
haaiuik
haaahkh

7.4 行范围模式

awk ‘/正则1/,/正则2/{Action}’
从匹配到正则1的行开始到匹配到正则2的行结束,不管是正则1还是正则2都以第一次匹配到的行为准

[root@localhost ~]# cat test
abc 12345 gg
cde 897 ll vv
rng 90 44 yyds xn 897
kkl fd 000
000 jkl cdk
[root@localhost ~]# awk '/897/,/000/{print $0}' test
cde 897 ll vv
rng 90 44 yyds xn 897
kkl fd 000

8、动作(Action)

  • 控制语句

条件判断,awk 本身就是一门编程语言,所以也是支持 if 语句的,只不过写在了一行

[root@localhost ~]# cat test
abc 12345 gg
cde 897 ll vv
rng 90 44 yyds xn 897
kkl fd 000
000 jkl cdk
[root@localhost ~]# awk '{if(NR>3) print $0}' test
kkl fd 000
000 jkl cdk
[root@localhost ~]# awk '{if(NR==3) {print $1;print $2}}' test
rng
90

if 对应的条件用小括号包起来,if 对应的大括号中有多条语句,if语法中的大括号不能省略,但是,如果 if 对应的大括号中只有一条命令,那么 if 对应的大括号则可以省略。

使用 if...else 语句判断哪些是系统用户,哪些是普通用户
[root@localhost ~]# awk -F ":" '{ if($3 < 500) {print $1,"系统用户"} else {print $1,"普通用户"} }' /etc/passwd
root 系统用户
bin 系统用户
daemon 系统用户
adm 系统用户
lp 系统用户
sync 系统用户
shutdown 系统用户
halt 系统用户
mail 系统用户
operator 系统用户
games 系统用户
ftp 系统用户
nobody 普通用户
dbus 系统用户
systemd-coredump 普通用户
systemd-resolve 系统用户
tss 系统用户
polkitd 普通用户
unbound 普通用户
sssd 普通用户
sshd 系统用户
rngd 普通用户
nginx 普通用户

for 和 while 循环

[root@localhost ~]# awk 'BEGIN{ for( i=1;i<=5;i++) {print i}}'
1
2
3
4
5
[root@localhost ~]# awk 'BEGIN{ i=1;while(i<=5) {print i++}}'
1
2
3
4
5

do…while的示例如下,它与while循环的不同之处在于,
while循环只有当满足条件时才会执行对应语句,而do…while循环则是无论是否满足条件,
都会先执行一遍do对应的代码,然后再判断是否满足while中对应的条件,
满足条件,则执行do对应的代码,如果不满足条件,则不再执行do对应的代码
[root@localhost ~]# awk 'BEGIN{ i=6; do{print "hello wrold";i++} while ( i <= 5 )}'
hello wrold
[root@localhost ~]# awk 'BEGIN{ i=1; do{print "hello wrold";i++} while ( i <= 5 )}'
hello wrold
hello wrold
hello wrold
hello wrold
hello wrold

for循环之continue和break

[root@localhost ~]# awk 'BEGIN{ for(i=1;i<=5;i++) { if(i==2) continue;print i } }'
1
3
4
5

[root@localhost ~]# awk 'BEGIN{ for(i=1;i<=5;i++) { if(i==3) break;print i } }'
1
2

exit的用法

[root@localhost ~]# awk 'BEGIN{print "hello world";print 1;print 2}'
hello world
1
2
[root@localhost ~]# awk 'BEGIN{print "hello world";exit;print 1;print 2}'
hello world

[root@localhost ~]# cat test
abc 12345 gg
cde 897 ll vv
rng 90 44 yyds xn 897
kkl fd 000
000 jkl cdk
[root@localhost ~]# awk 'BEGIN{print "hello world"}{print $0} END {print "over"}' test
hello world
abc 12345 gg
cde 897 ll vv
rng 90 44 yyds xn 897
kkl fd 000
000 jkl cdk
over
[root@localhost ~]# awk 'BEGIN{print "hello world";exit} {print $0} END {print "over"}' test
hello world
over

如果使用了 END 模式,exit 之后的都不会执行,但是 END 后面的内容除外。
没有使用 END 模式,exit之后的内容不会执行,直接退出当前 awk

next的用法,不处理当前行,直接跳到处理下一行

[root@localhost ~]# cat test
abc 12345 gg
cde 897 ll vv
rng 90 44 yyds xn 897
kkl fd 000
000 jkl cdk

[root@localhost ~]# awk '{ if(NR==2) next ; print $0}' test
abc 12345 gg
rng 90 44 yyds xn 897
kkl fd 000
000 jkl cdk

9、数组

在awk中,元素的值可以为空,所以不能根据元素的值是否为空去判断一个元素是否存在;当一个元素不存在于数组时,如果我们直接引用这个不存在的元素,awk会自动创建这个元素,并且默认为这个元素赋值为”空字符串”

[root@localhost ~]# awk 'BEGIN{ subject[0]="语文";subject[1]="数学";subject[2]="英语";subject[3]="历史";subject[4]="";print subject[2] }'
英语
[root@localhost ~]# awk 'BEGIN{ subject[0]="语文";subject[1]="数学";subject[2]="英语";subject[3]="历史";subject[4]="";print subject[4] }'

通过以下语句去判断一个元素是否存在是错误的
[root@localhost ~]# awk 'BEGIN{ subject[0]="语文";subject[1]="数学";subject[2]="英语";subject[3]="历史";subject[4]="";if(subject[4]==0); print "subject数组的第5个元素不存在" }'
subject数组的第5个元素不存在

可以看到当一个元素不存在于数组时,如果我们直接引用这个不存在的元素,awk会自动创建这个元素,并且默认为这个元素赋值为”空字符串”
[root@localhost ~]# awk 'BEGIN{ subject[0]="语文";subject[1]="数学";subject[2]="英语";subject[3]="历史";subject[4]="";if(subject[6]==0); print "subject数组的第7个元素不存在" }'
subject数组的第7个元素不存在

通过以下语法去判断一个元素是否存在
if(下标 in 数组名)
if(!(下标 in 数组名))

[root@localhost ~]# awk 'BEGIN{ subject[0]="语文";subject[1]="数学";subject[2]="英语";subject[3]="历史";subject[4]="";if( 4 in subject ) {print "subject数组的第5个元素存在"} }'
subject数组的第5个元素存在
[root@localhost ~]# awk 'BEGIN{ subject[0]="语文";subject[1]="数学";subject[2]="英语";subject[3]="历史";subject[4]="";if( !(5 in subject) ) {print "subject数组的第6个元素不存在"} }'
subject数组的第6个元素不存在

在awk中,数组的下标不仅可以为数字,可以为任意的字符串

[root@localhost ~]# awk 'BEGIN{ subject["学科1"]="语文";subject["学科2"]="数学";subject["学科3"]="英语";subject["学科4"]="历史";subject["学科5"]=""; print subject["学科1"] }'
语文

使用delete可以删除数组中的元素,也可以删除整个数组

[root@localhost ~]# awk 'BEGIN{ subject["学科1"]="语文";subject["学科2"]="数学";subject["学科3"]="英语";subject["学科4"]="历史";subject["学科5"]=""; print subject["学科1"]; delete subject["学科1"]; print subject["学科1"] }'
语文
      #此处是空,是经过删除后的输出
[root@localhost ~]# awk 'BEGIN{ subject["学科1"]="语文";subject["学科2"]="数学";subject["学科3"]="英语";subject["学科4"]="历史";subject["学科5"]=""; print subject["学科1"]; delete subject; print subject["学科3"] }'
语文
     #此处是空,是经过删除后的输出

使用 for 循环遍历数组中的每个元素
for循环语法格式1,方式1适用于数组下标都是数字
for(初始化; 布尔表达式; 更新) {
//代码语句
}

for循环语法格式2,下标是字符串就可以使用方式2
for(变量 in 数组) {
//代码语句
}

示例中 i 代表元素的下标
[root@localhost ~]# awk 'BEGIN{ subject["学科1"]="语文";subject["学科2"]="数学";subject["学科3"]="英语";subject["学科4"]="历史";subject["学科5"]=""; for( i in subject ){print i,subject[i]} }'
学科1 语文
学科2 数学
学科3 英语
学科4 历史
学科5

数组实际应用场景,在实际工作中,统计某些字符出现的次数,比如,我们想要统计日志中每个IP地址出现了多少次,我们就可以利用数组去统计,在awk中,可以利用数组进行数值运算,参与运算的字符串(下标),字符串将被当做数字0进行运算,空字符串也会当做数字0参与运算

[root@localhost ~]# awk 'BEGIN{ a=1;print a;a++;print a}'
1
2
[root@localhost ~]# awk 'BEGIN{ a="lxr";print a;a++;print a}'
lxr
1
[root@localhost ~]# awk 'BEGIN{ a="lxr";print a;a++;print a;a=a+2;print a }'
lxr
1
3
[root@localhost ~]# awk 'BEGIN{ a="";print a;a++;print a;a=a+2;print a }'

1
3

当一个元素不存在时,这个元素会被赋值空字符串,然后参与运算
[root@localhost ~]# awk 'BEGIN{ print a;a++;print a;a=a+2;print a }'

1
3

统计ip重复了多少次

[root@localhost ~]# cat ip.txt
192.168.249.1
192.168.249.33
192.168.249.123
192.168.249.2
192.168.249.12
192.168.249.14
192.168.249.12
192.168.249.123
192.168.249.133
192.168.249.33
192.168.249.12
192.168.249.12
192.168.249.14

[root@localhost ~]# awk '{ count[$1]++ } END { for( i in count){print i,count[i]}  }' ip.txt
192.168.249.123 2
192.168.249.133 1
192.168.249.12 4
192.168.249.1 1
192.168.249.14 2
192.168.249.2 1
192.168.249.33 2

统计人名出现的次数

[root@localhost ~]# cat name
BOb Shelly
Halun BOb Feife Shelly
Even Kobe James
Kobe Pull James
Davies Shelly Halun lxr

[root@localhost ~]# awk '{ for(i=1;i<=NF;i++) { count[$i]++ } } END { for (n in count){print n,count[n]}}' name
James 2
BOb 2
Pull 1
Feife 1
Halun 2
Shelly 3
lxr 1
Kobe 2
Davies 1
Even 1

10、取行取列

取行:NR==1,NR>=1,$3>=1000,$3 ~ /1000/(第三列包含1000,不包含用 !~ 表示)
取列:$1,$NF,$(NF-1)


free | awk '$3>=0 && /Swap/ {print "swap already used!"}'
swap already used!

参考链接如下:
https://www.zsythink.net/archives/tag/awk

<think>嗯,用户想学习或练习Linux下的awk用法,需要提供示例和教程。首先,我得回忆一下awk的基本概念和工作原理。记得awk是一种文本处理工具,逐行处理数据,按字段进行操作。用户可能需要从基础开始,逐步深入。 然后,看看用户给的参考引用。引用[1]提到awk的工作原理,引用[2]展示了一个打印字段的例子,引用[3]则是一些更复杂的应用,比如处理ifconfig的输出。这些例子应该可以拆解成不同的练习题目,帮助用户逐步掌握。 接下来,得确保回答结构清晰。先介绍awk的基本用法,再提供由浅入深的示例。比如,从打印特定字段开始,到条件过滤、计算统计,再到文本处理的高级技巧,比如处理多行记录或正则表达式匹配。 还要注意用户的练习需求,可能需要实际问题来练习。例如,处理学生成绩表、日志分析、系统信息提取等,这些实际场景能帮助用户巩固知识。同时,结合参考中的例子,比如处理ifconfig的输出,可以设计相应的练习题。 另外,用户提到的示例中的多行处理(使用RS变量)和字段处理(FS变量)也很重要。需要详细说明这些概念,并提供示例代码和解释,确保用户理解如何修改记录分隔符和字段分隔符。 最后,生成相关问题,帮助用户进一步探索awk的高级功能,比如性能优化、复杂文本处理等。还要确保所有命令和示例符合LaTeX格式要求,行内公式用$...$,独立公式用$$...$$,并且正确引用来源。</think>### Linux awk 基础与进阶练习指南 ### 一、awk 基础用法 1. **核心原理** awk 按记录(默认行)处理文本,每条记录自动分割为字段(默认空格分隔),支持模式匹配与动作执行[^1]。基本结构: ```bash awk 'pattern {action}' file ``` 2. **字段操作示例** ```bash awk '{print $1, $3}' data.txt # 输出第1和第3列[^2] awk -F: '{print $1,$6}' /etc/passwd # 使用冒号分隔符 ``` ### 二、进阶练习示例 #### 1. 条件过滤 ```bash # 筛选成绩大于80的学生 awk '$3 > 80 {print $1, $3}' scores.txt # 排除空行 awk '!/^$/ {print}' log.txt ``` #### 2. 数值计算 ```bash # 计算文件总行数 awk 'END {print NR}' data.txt # 求第2列平均值 awk '{sum+=$2} END {print sum/NR}' numbers.txt ``` #### 3. 多字段处理 ```bash # 组合字段输出 awk '{print "用户:" $1, "UID:" $3}' /etc/passwd # 字段重排(交换第2、3列) awk '{tmp=$2; $2=$3; $3=tmp; print}' data.txt ``` #### 4. 系统信息提取(参考[^3]) ```bash # 获取所有IPv4地址(排除127) ifconfig | awk '/inet / && !($2 ~ /^127/) {print $2}' # 统计TCP连接数 netstat -ant | awk '$6 == "ESTABLISHED" {count++} END {print count}' ``` ### 三、高级技巧 1. **多行记录处理** 修改记录分隔符 `RS`: ```bash awk 'BEGIN{RS=""; FS="\n"} {print $2}' multi.txt # 处理段落格式数据 ``` 2. **数组应用** ```bash # 统计词频 awk '{for(i=1;i<=NF;i++) words[$i]++} END {for(w in words) print w, words[w]}' text.txt ``` 3. **正则表达式匹配** ```bash # 匹配带版本号的行 awk '/[0-9]+\.[0-9]+\.[0-9]+/ {print}' version.log ``` ### 四、实践项目 1. **日志分析** 分析 Nginx 访问日志: ```bash awk '$9 == 404 {print $7}' access.log | sort | uniq -c | sort -nr ``` 2. **数据报表生成** ```bash awk 'BEGIN{print "姓名\t总分"} {sum=$2+$3+$4; print $1 "\t" sum}' scores.txt ``` 3. **系统监控脚本** ```bash df | awk '$5 > 80 {print "警告: " $1 " 使用率 " $5}' ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值