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,输出分隔符,默认空白字符
[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 输出换行符
默认的换行符 依然保留 [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 ",$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 ",$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 |
[root@localhost ~]# awk -F: '{$3>1000?usertype="nomal user":usertype="sysadmin or sysuser";printf "%-6s : %s ",$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---------- "}{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 ",$1;else {printf "root or sysuser : %s ",$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 ",$1} else {printf "root or sysuser : %s ",$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