awk命令

zhiliang 2020-01-31

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

语法:

awk [options] -f ‘program‘ filename

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

选项:

-F:指定输入分隔符

-v  VAR=value:自定义变量

常用命令

1、print

print item1,item2,......

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

2、变量

内置变量

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

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

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

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



[ ~]# 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依然保留
[ ~]# vim num.txt 
1 a b c
2 de fg hj
3
4
5
6

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

3
4
5
6

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

NF:number of field 字段数量

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

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

 NR: number of record 行数,行号

 FNR: 各文件分别计数

[ ~]# awk  ‘{print NR}‘ num.txt
1
2
3
4
5
6

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

对每个文件单独计数
[ ~]# 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遍历文件每一行的特性
[ ~]# 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参数数组,保存命令行给定的所有参数

[ ~]# awk ‘{print ARGC}‘ /etc/fstab /etc/issue
3
3
3
3
3
3
3
3
3
3
3
3
3
3

[ ~]# awk ‘BEGIN{print ARGC}‘ /etc/fstab /etc/issue
3

[ ~]# awk ‘BEGIN{print ARGV[0]}‘ /etc/fstab /etc/issue
awk
[ ~]# awk ‘BEGIN{print ARGV[1]}‘ /etc/fstab /etc/issue
/etc/fstab
[ ~]# awk ‘BEGIN{print ARGV[2]}‘ /etc/fstab /etc/issue
/etc/issue  

 自定义变量

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

(2)在program中定义

[ ~]# awk -v test="hello world" ‘BEGIN{print test}‘
hello world

[ ~]# awk ‘BEGIN{test="hello world";print test}‘
hello world  

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

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



[ ~]# 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语句的时候

[ ~]# 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


[ ~]# 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  

 操作符

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

  ~:匹配 、!~:不匹配

逻辑操作符&&、||、!
条件表达式selector?if-true-expression:if-false-expression
  
[ ~]# 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:空匹配,匹配每一行

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

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

[ ~]# 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或非空为真

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

标准写法~符号后的/匹配字符串/不加引号
[ ~]# 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/指定地址

相当于空匹配
[ ~]# 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

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

[host ~]# 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{}:仅在处理文件中的文本之前执行一次
[ ~]# awk -F: ‘BEGIN{print "------username-------uid----------\n"}{print $1,$3}‘ /etc/passwd
------username-------uid----------

root 0
bin 1
daemon 2
adm 3

当不加BEGIN的时候会对每一行都执行每一条语句
[ ~]# 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{}:文本处理完成之后执行一次
[ ~]# 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中的语句
[ ~]# 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}

[ ~]# awk -F : ‘{if ($3>1000) print $1,$3}‘ /etc/passwd
test1 1001
其中,print或printf 是最常用的编辑指令;若有多条编辑指令,可用分号分隔,或者把每一条语句用花括号括起来
[ ~]# 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

[ ~]# 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}

统计字段长度
[ro ~]# 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的
[ ~]# 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)

[ ~]# 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}

[ ~]# 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

[ ~]# 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为偶数的用户
[ ~]# 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会自动创建此元素,并将其值初始化为空串

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


遍历数组中的元素,其中i为数组data的索引,
[ ~]# awk ‘BEGIN{data["name"]="tom";data["age"]="18"; for (i in data) print data[i]}‘
18
tom
[ ~]# vim num.txt 
a 1
b 15
a 20
c 6
d 50
b 3
a 7
统计字母出现的次数
[ ~]# awk ‘{a[$1]++}END {for (i in a) print i,a[i]}‘ num.txt 
a 3
b 2
c 1
d 1
统计每个字母对应的数字和
[ ~]# awk ‘{a[$1]+=$2}END {for (i in a) print i,a[i]}‘ num.txt 
a 28
b 18
c 6
d 50
[ ~]# awk -F ‘[ #/:,-=]+‘ ‘{for (a=1;a<= NF;a++) {word[$a]++}}END{for (i in word) print i,word[i]}‘ /etc/fstab aff 1 14cd 1bba 1man 1bbd 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

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


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

[ ~]# 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。
[ ~]# awk -F: ‘{gsub(/o/,"O",$1);print $1}‘ /etc/passwdrOOtbindaemOnadmlpsyncshutdOwnhaltmailOperatOr

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

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

  

相关推荐