hopeless-dream

导航

awk命令

awk是一个强大的报告生成工具,用于格式化文本输出

语法:

  awk [options] -f 'program' filename

  program由{ pattern + action statements}组成,动作语句之间用分号“;”分隔

action如果不指定,则输入每一个pattern匹配的输入行

[root@docker04 ~]# vim a.txt
Beth 4.00 0
Dan 3.75 0
Kathy 4.00 10
Mark 5.00 20
Mary 5.50 22
Susie 4.25 18

[root@docker04 ~]# awk '$3 > 0' a.txt 
Kathy 4.00 10
Mark 5.00 20
Mary 5.50 22
Susie 4.25 18

 

选项:

  -F:指定输入分隔符

  -v  VAR=value:自定义变量

 

常用命令

1、print

  print item1,item2,......

  item之间用逗号分隔,如果省略item,相当于print $0

2、变量

  内置变量

    FS:input field seperator,输入分隔符,与-F指定的相同,默认是空白字符 

    OFS:output field seperator,输出分隔符,默认空白字符

[root@localhost ~]# awk -v FS=: '{print $1}' /etc/passwd 
root
bin
daemon
adm

[root@localhost ~]# awk -F : '{print $1,$3}' /etc/passwd
root 0
bin 1
daemon 2
adm 3



[root@localhost ~]# awk -v FS=: -v OFS=" | " '{print $1,$3}' /etc/passwd
root | 0
bin | 1
daemon | 2
adm | 3
lp | 4

  RS input record seperator 输入换行符

  ORS output record seperator 输出换行符

默认的换行符\n依然保留
[root@localhost ~]# vim num.txt 
1 a b c
2 de fg hj
3
4
5
6

[root@localhost ~]# awk -v RS=" " '{print}' num.txt
1
a
b
c
2
de
fg
hj

3
4
5
6

[root@localhost ~]# awk -v RS=" " -v ORS='#' '{print}' num.txt
1#a#b#c
2#de#fg#hj#
3
4
5
6
#

  NF:number of field 字段数量

打印字段数量
[root@localhost ~]# awk  '{print NF}' num.txt
1
1
1
2
1
1
1
4

打印最后一个字段
[root@localhost ~]# awk  '{print $NF}' num.txt
c
hj
3
4
5
6

 NR: number of record 行数,行号

 FNR: 各文件分别计数

[root@localhost ~]# awk  '{print NR}' num.txt
1
2
3
4
5
6

多个文件
[root@localhost ~]# awk  '{print NR}' num.txt /etc/fstab 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

对每个文件单独计数
[root@localhost ~]# awk  '{print FNR}' num.txt /etc/fstab 
1
2
3
4
5
6
1
2
3
4
5
6
7
8
9
10
11

 FILENAME: 当前正在处理的文件名

体现了awk遍历文件每一行的特性
[root@localhost ~]# awk  '{print FILENAME}' num.txt /etc/fstab 
num.txt
num.txt
num.txt
num.txt
num.txt
num.txt
/etc/fstab
/etc/fstab
/etc/fstab
/etc/fstab
/etc/fstab
/etc/fstab
/etc/fstab
/etc/fstab
/etc/fstab
/etc/fstab
/etc/fstab

 ARGC:命令行参数个数

 ARGV:awk参数数组,保存命令行给定的所有参数

[root@localhost ~]# awk '{print ARGC}' /etc/fstab /etc/issue
3
3
3
3
3
3
3
3
3
3
3
3
3
3

[root@localhost ~]# awk 'BEGIN{print ARGC}' /etc/fstab /etc/issue
3

[root@localhost ~]# awk 'BEGIN{print ARGV[0]}' /etc/fstab /etc/issue
awk
[root@localhost ~]# awk 'BEGIN{print ARGV[1]}' /etc/fstab /etc/issue
/etc/fstab
[root@localhost ~]# awk 'BEGIN{print ARGV[2]}' /etc/fstab /etc/issue
/etc/issue  

 自定义变量

  (1)-v var=value 变量名区分大小写

  (2)在program中定义

[root@localhost ~]# awk -v test="hello world" 'BEGIN{print test}'
hello world

[root@localhost ~]# awk 'BEGIN{test="hello world";print test}'
hello world  

求极值
[root@localhost ~]# cat num.txt 
a 1
b 15
a 20
c 6
d 50
b 3
a 7

[root@localhost ~]# awk 'BEGIN{min=999;if ($2 <min) min=$2}END{print "最小值为: ",min}' num.txt 
最小值为: a



[root@localhost ~]# awk 'BEGIN{max=0}{if ($2 >max) max=$2 }END{print "最大值为: ",max}' num.txt 
最大值为: 50

printf 命令

  格式化输出 printf “FORMAT”,item1,item2,... FORMAT需要为每一个item指定一个格式化符号

  格式符包括:

    %c:显示字符的ASCII码

    %d,%i:显示十进制整数

    %e,%E:科学计数法数值显示

    %f:显示为浮点数

    %g,%G:以科学计数法或浮点数显示数值

    %u:无符号整数

    %s:显示为字符串

    %%:显示%自身

  修饰符:- 表示左对齐 + 表示显示数值符号

另外打印的时候如果需要打印特殊字符,需要查ASCII码表,对应的十六进制,例如:\47------> '  \44 ------> $ \46 ---------> &,在某些场景需要使用,比如拼接SQL语句的时候

[root@localhost ~]# awk -F: '{printf "Username: %s, UID: %d\n",$1,$3}' /etc/passwd
Username: root, UID: 0
Username: bin, UID: 1
Username: daemon, UID: 2
Username: adm, UID: 3
Username: lp, UID: 4
Username: sync, UID: 5


[root@localhost ~]# awk -F: '{printf "Username: %-6s, UID: %d\n",$1,$3}' /etc/passwd
Username: root  , UID: 0
Username: bin   , UID: 1
Username: daemon, UID: 2
Username: adm   , UID: 3
Username: lp    , UID: 4
Username: sync  , UID: 5
Username: shutdown, UID: 6
Username: halt  , UID: 7  

注意 小数点也算长度内

[root@docker04 ~]# cat a.txt 
Beth 4.00 0
Dan 3.75 0
Kathy 4.00 10
Mark 5.00 20
Mary 5.50 22
Susie 4.25 18

[root@docker04 ~]# awk '{ printf("%-8s $%7.2f\n", $1, $2 * $3) }' a.txt 
Beth     $   0.00
Dan      $   0.00
Kathy    $  40.00
Mark     $ 100.00
Mary     $ 121.00
Susie    $  76.50

 

操作符

 

操作符名称 包含的符号
算术操作符 +、-、*、/、^、%      -x:负值      +x:转换为数值
赋值操作符 =、+=、-、-=、*=、/=、%= 、++、--
比较操作符 >,>=、<、<=、!=、==
模式匹配符

    ~:匹配 、!~:不匹配

逻辑操作符 &&、||、!
条件表达式 selector?if-true-expression:if-false-expression
   
[root@localhost ~]# awk -F: '{$3>1000?usertype="nomal user":usertype="sysadmin or sysuser";printf "%-6s : %s\n",$1,usertype}' /etc/passwd
chrony : sysadmin or sysuser
mysql  : sysadmin or sysuser
ntp    : sysadmin or sysuser
test   : sysadmin or sysuser
test1  : nomal user

pattern

1、empty:空匹配,匹配每一行

[root@localhost ~]# awk -F: '{print $1}' /etc/passwd
root
bin
daemon
adm
lp
sync
shutdown
halt
mail

2、/regular expression/:仅处理匹配到的行

[root@localhost ~]# awk -F '[= ]+' '/^UUID/{print $2}' /etc/fstab 
0bbd5e50-606c-4c47-8cd7-1ae67f812437
bba2c917-8540-41c8-97e6-f1d73d9143ba
1c0f8351-49f0-4dd8-9a8b-1aff1d4a77b0

3、关系表达式:relation expression 返回boolean值,真值被处理,非0或非空为真

[root@localhost ~]# awk -F: '$NF=="/bin/bash"{print $1,$NF}' /etc/passwd
root /bin/bash
test /bin/bash
test1 /bin/bash

标准写法~符号后的/匹配字符串/不加引号
[root@localhost ~]# awk -F: '$NF~/bash$/{print $1,$NF}' /etc/passwd
root /bin/bash
test /bin/bash
test1 /bin/bash

4、地址定界:awk不支持1,3这样的写法,可以用NR或者使用/pat1/,/pat2/指定地址

相当于空匹配
[root@localhost ~]# awk '1,3{print}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt

[root@localhost ~]# awk 'NR>1&&NR<=3{print}' /etc/passwd
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin

[root@localhost ~]# awk '/^a/,/^s/{print}' /etc/passwd
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync

5、BEGIN/END模式

BEGIN{}:仅在处理文件中的文本之前执行一次
[root@localhost ~]# awk -F: 'BEGIN{print "------username-------uid----------\n"}{print $1,$3}' /etc/passwd
------username-------uid----------

root 0
bin 1
daemon 2
adm 3

当不加BEGIN的时候会对每一行都执行每一条语句
[root@localhost
~]# awk -F: '{print "------username-------uid----------"}{print $1,$3}' /etc/passwd ------username-------uid---------- root 0 ------username-------uid---------- bin 1 ------username-------uid---------- daemon 2 ------username-------uid---------- adm 3 END{}:文本处理完成之后执行一次
[root@localhost
~]# awk -F: '{print "------username-------uid----------"}{print $1,$3}END{print "@@@@@@@@done"}' /etc/passwd ------username-------uid---------- root 0 ------username-------uid---------- bin 1 ------username-------uid---------- daemon 2 ------username-------uid---------- adm 3 @@@@@@@@done 只在文本处理前和语句执行后分别执行BEGIN和END中的语句
[root@localhost
~]# awk -F: 'BEGIN{print "------username-------uid----------"}{print $1,$3}END{print "@@@@@@@@done"}' /etc/passwd ------username-------uid---------- root 0 bin 1 daemon 2 adm 3 @@@@@@@@done 

常用的action

  (1)expressions  

  (2)控制语句       if、while等

  (3)组合语句

  (4)输入语句

  (5)输出语句

 

控制语句

  语法:

    if (condition) {statements} 或者 if (condition) {statements} else {statements}

[root@localhost ~]# awk -F : '{if ($3>1000) print $1,$3}' /etc/passwd
test1 1001
其中,print或printf 是最常用的编辑指令;若有多条编辑指令,可用分号分隔,或者把每一条语句用花括号括起来
[root@localhost ~]# awk -F : '{if ($3>1000) printf "common_user: %s\n",$1;else {printf "root or sysuser : %s\n",$1}}' /etc/passwd
root or sysuser : postfix
root or sysuser : chrony
root or sysuser : mysql
root or sysuser : ntp
root or sysuser : test
common_user: test1

[root@localhost ~]# awk -F : '{if ($3>1000) {printf "common_user: %s\n",$1} else {printf "root or sysuser : %s\n",$1}}' /etc/passwd
root or sysuser : mysql
root or sysuser : ntp
root or sysuser : test
common_user: test1

    while (condition) {statements}

统计字段长度
[root@localhost ~]# awk '/[[:space:]]+linux16/ {i=1;while(i<=NF) {print $i,length($i);i++}}' /etc/grub2.cfg 
linux16 7
/vmlinuz-3.10.0-862.el7.x86_64 30
root=UUID=0bbd5e50-606c-4c47-8cd7-1ae67f812437 46
ro 2
crashkernel=auto 16
net.ifnames=0 13
rhgb 4
quiet 5
LANG=en_US.UTF-8 16

统计字段长度大于5的
[root@localhost
~]# awk '/[[:space:]]+linux16/ {i=1;while(i<=NF) {if (length($i)>5) print $i,length($i);i++}}' /etc/grub2.cfg linux16 7 /vmlinuz-3.10.0-862.el7.x86_64 30 root=UUID=0bbd5e50-606c-4c47-8cd7-1ae67f812437 46 crashkernel=auto 16 net.ifnames=0 13 LANG=en_US.UTF-8 16 linux16 7 /vmlinuz-0-rescue-1f41634c945240cf95b0a976b07104ea 50 root=UUID=0bbd5e50-606c-4c47-8cd7-1ae67f812437 46 crashkernel=auto 16 net.ifnames=0 13

    do {statements} while (condition)

[root@localhost ~]# awk '/[[:space:]]+linux16/ {i=1;do {if (length($i)>5) print $i,length($i);i++} while(i<=NF)}' /etc/grub2.cfg 
linux16 7
/vmlinuz-3.10.0-862.el7.x86_64 30
root=UUID=0bbd5e50-606c-4c47-8cd7-1ae67f812437 46
crashkernel=auto 16
net.ifnames=0 13
LANG=en_US.UTF-8 16
linux16 7
/vmlinuz-0-rescue-1f41634c945240cf95b0a976b07104ea 50
root=UUID=0bbd5e50-606c-4c47-8cd7-1ae67f812437 46
crashkernel=auto 16
net.ifnames=0 13

    for (expr1;expr2;...;exprn) {statements}

[root@localhost ~]# awk '/[[:space:]]+linux16/ {for (i=1;i<=NF;i++) print $i,length($i)}' /etc/grub2.cfg 
linux16 7
/vmlinuz-3.10.0-862.el7.x86_64 30
root=UUID=0bbd5e50-606c-4c47-8cd7-1ae67f812437 46
ro 2
crashkernel=auto 16
net.ifnames=0 13
rhgb 4
quiet 5
LANG=en_US.UTF-8 16
linux16 7
/vmlinuz-0-rescue-1f41634c945240cf95b0a976b07104ea 50
root=UUID=0bbd5e50-606c-4c47-8cd7-1ae67f812437 46
ro 2
crashkernel=auto 16
net.ifnames=0 13
rhgb 4
quiet 5

[root@localhost ~]# awk '/[[:space:]]+linux16/ {for (i=1;i<=NF;i++) if (length($i)>5) print $i,length($i)}' /etc/grub2.cfg 
linux16 7
/vmlinuz-3.10.0-862.el7.x86_64 30
root=UUID=0bbd5e50-606c-4c47-8cd7-1ae67f812437 46
crashkernel=auto 16
net.ifnames=0 13
LANG=en_US.UTF-8 16
linux16 7
/vmlinuz-0-rescue-1f41634c945240cf95b0a976b07104ea 50
root=UUID=0bbd5e50-606c-4c47-8cd7-1ae67f812437 46
crashkernel=auto 16
net.ifnames=0 13

使用for循环遍历数组元素

  for (var in array)

 

switch语句

  语法 switch (expression) {case value1 or /regexp/: statement1;case value1 or /regexp/: statement2;...;default: statement}

next:提前结束本行的处理,对下一行进行处理

统计uid为偶数的用户
[root@localhost ~]# awk -F : '{if ($3%2!=0) next;print $1,$3}' /etc/passwd
root 0
daemon 2
lp 4
shutdown 6
mail 8
games 12
ftp 14
systemd-network 192
sshd 74
chrony 998
ntp 38
test 1000

数组

关联数组: array[index]

  索引index

    (1)可以使用任意字符串,但字符串要使用双引号

    (2)如果数组元素不存在,在引用时,awk会自动创建此元素,并将其值初始化为空串

自定义数组
[root@localhost ~]# awk 'BEGIN{data["name"]="tom";data["age"]="18";print data["name"]}'
tom


遍历数组中的元素,其中i为数组data的索引,
[root@localhost ~]# awk 'BEGIN{data["name"]="tom";data["age"]="18"; for (i in data) print data[i]}'
18
tom

[root@localhost ~]# vim num.txt 
a 1
b 15
a 20
c 6
d 50
b 3
a 7

统计字母出现的次数 [root@localhost ~]# awk '{a[$1]++}END {for (i in a) print i,a[i]}' num.txt a 3 b 2 c 1 d 1
统计每个字母对应的数字和 [root@localhost
~]# awk '{a[$1]+=$2}END {for (i in a) print i,a[i]}' num.txt a 28 b 18 c 6 d 50

[root@localhost ~]# awk -F '[ #/:,-=]+' '{for (a=1;a<= NF;a++) {word[$a]++}}END{for (i in word) print i,word[i]}' /etc/fstab
aff 1
14
cd 1
bba 1
man 1
bbd 1

函数

 内置函数

  字符串处理:length([string]) 返回string的长度

sub (regular expression, substitution string):

sub(/regular expression/,"substitution string", target string) 在目标字符串中查找第一个指定匹配的字符串,并替换成指定子串

awk '{ sub(/test/, "mytest"); print }' testfile

在整个记录中匹配,替换只发生在每行第一次匹配发生的时候。

awk '{ sub(/test/, "mytest", $1); print }' testfile

在整个记录的第一个域中进行匹配,替换只发生在每行第一次匹配发生的时候


[root@localhost ~]# echo "0001|20081223efskjfdj|912EREADFASDLKJCV"|awk -F '|' 'BEGIN{ OFS="|" } {sub(/[0-9]+/,"",$2);print $0}'
0001|efskjfdj|EREADFASDLKJCV

[root@localhost ~]# awk -F: '{sub(/o/,"O",$1);print $1}' /etc/passwd
rOot
bin
daemOn

gsub (/regular expression/, "substitution string", target string)在整个文档中查找指定字段中所有能匹配指定字符串的字段,并替换成指定子串

awk '{ gsub(/test/, "mytest"); print }' testfile
在整个文档中匹配test,匹配的都被替换成mytest。

awk '{ gsub(/test/, "mytest", $1); print }' testfile
在整个文档的第一个域中匹配,所有匹配的都被替换成mytest。

[root@localhost ~]# awk -F: '{gsub(/o/,"O",$1);print $1}' /etc/passwd
rOOt
bin
daemOn
adm
lp
sync
shutdOwn
halt
mail
OperatOr

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

[root@localhost ~]# echo "0001|20081223efskjfdj|EREADFASDLKJCV"|awk '{split($0,a,"|");print a[1],a[2],a[3]}'
0001 20081223efskjfdj EREADFASDLKJCV

 

 

    

 

posted on 2020-01-31 15:22  hopeless-dream  阅读(237)  评论(0编辑  收藏  举报