AWK
有其他的衍生版本,比如nawk
和gawk
,在Linux发行版里,默认使用的是gawk
:
# ls -l `which awk`
lrwxrwxrwx. 1 root root 4 5月 9 20:25 /usr/bin/awk -> gawk
AWK
是按行处理文本,将行按照某一分隔符进行分割,其中分割之后的第一部分为$1
,第二部分为$2
,依次类推,其中$0
表示整行,超过10的要将数字用括号括起来,比如$(10)
AWK
默认的分隔符是空格和tab
,其中操作命令必须写在{}里面,语法:awk '/模式/{print $1}' file //模式支持正则表达式,只对含有该模式的行进行操作
-F
选项指定分隔符:awk -F: '/模式/{print $1}' file
常见的操作符有:~
、!~
、==
、!=
、>
、<
、++
、--
等
~
和!~
表示包含和不包含:# awk -F: '$1~/ro/{print $0}' passwd
root:x:0:0:root:/root:/bin/bash
==
和!=
表示严格的等于和不等于:# awk -F: '$3==1{print $0}' passwd
bin:x:1:1:bin:/bin:/sbin/nologin
# awk -F: '{$3++;print $3}' passwd
1
2
3
4
5
6
&&
或||
BEGIN
中的命令是在AWK读取文本的第一行之前进行操作:# awk -F: 'BEGIN{print "用户名"}{print $1}' passwd
用户名
root
bin
daemon
adm
lp
sync
END
中的命令是在读取完最后一行之后进行的操作:# awk -F: '{print $1}END{print "结束"}' passwd
root
bin
daemon
adm
lp
sync
结束
# awk -F: 'BEGIN{a=1}{print a}' passwd
1
1
1
1
1
1
如何没有预先定义变量的话,在后面也是可以引用的,只不过这个变量的值为空。引用变量的时候不加$
END
中也可以进行最终的计算:
# awk -F: 'BEGIN{sum=0}{sum+=$3}END{print "所有UID的和为:"sum}' passwd
所有UID的和为:15
或者不预先定义变量:
# awk -F: '{sum+=$3}END{print "所有UID的和为:"sum}' passwd
所有UID的和为:15
FS
代表分隔符:# awk 'BEGIN{FS=":"}{print $1}' passwd
root
bin
daemon
adm
lp
sync
OFS
为打印输出的分隔符,默认的是空格:# awk 'BEGIN{FS=":";OFS="---"}{print $1,$2}' passwd
root---x
bin---x
daemon---x
adm---x
lp---x
sync---x
NF
的值表示将每一行按照FS
指定的分隔符分为多少段:# awk 'BEGIN{FS=":"}{print NF}' passwd
7
7
7
7
7
7
如果要打印每一行的最后一段,可以使用以下用法:
# awk 'BEGIN{FS=":"}{print $NF}' passwd
/bin/bash
/sbin/nologin
/sbin/nologin
/sbin/nologin
/sbin/nologin
/bin/sync
RS
的值,默认为\n
:# cat test.txt
1,张三:经理 2,李四:职员 3,王五:老板
# awk -F[:,] 'BEGIN{RS=" "}{print $2,$3}' test.txt
张三 经理
李四 职员
王五 老板
ORS
变量的值决定输出行的分隔符,默认的为\n
:# awk 'BEGIN{ORS="---"}{print $0}' 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---
FILENAME
变量的值为当前操作文件的文件名:# awk -F: '{print FILENAME}' passwd
passwd
passwd
passwd
passwd
passwd
passwd
NR
变量的值表示当前正在操作的行号:# awk -F: '{print NR}' passwd
1
2
3
4
5
6
如果这里将行分隔符RS
赋值为-
,验证NR
的值:
# awk 'BEGIN{RS="-"}{print NR}' passwd
1
FNR
也表示当前操作的行号,不同的是,当对一个文件进行操作的时候FNR
和NR
的值是一样的。当对两个文件进行操作时,FNR
和NR
的值是不一样的:# awk -F: '{print NR,$1,FNR}' passwd shadow
1 root 1
2 bin 2
3 daemon 3
4 adm 4
5 lp 5
6 sync 6
7 root 1
8 bin 2
9 daemon 3
10 adm 4
11 lp 5
12 sync 6
if
语句:if(判断1){语句1}else if(判断2){语句2}else{语句3}
if(判断1)语句1;else if(判断2)语句2;else语句3
# awk -F: '{if($3==0){print $1"是root"}else if($3>=1000){print $1"是普通用户"}else{print $1"是系统用户"}}' passwd
root是root
bin是系统用户
daemon是系统用户
adm是系统用户
lp是系统用户
sync是系统用户
for
循坏语句:awk -F: '{for(i=1;i<=NF-5;i++){print $i}}' passwd
root
x
bin
x
daemon
x
while
循坏语句:# awk -F: '{print "------------";i=1;while(i<=NF){print $i;i++}}' 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
break
:# echo -e "1 2 3 4 1\n5 6 7" | awk '{for(i=1;i<=NF;i++){if($i>=2){break}print $i}}'
1
continue
:如果遇到continue,continue后面的语句不再执行,继续下一个循环# echo -e "1 2 3 4 1\n5 6 7" | awk '{for(i=1;i<=NF;i++){if($i>=2){continue}print $i}}'
1
1
next
:遇到next,跳出循环,继续下移循环,在循环语句中next和break语句效果一样。区别在于,break和continue语句只能用于循环语句,next语句还可以用于其他语句,以下例子用于判断语句# echo -e "1 2 3 4 1\n5 6 7 1" | awk '{if($2<=2){next}print $0}'
5 6 7 1
# awk -F: 'NR==FNR{print NR,FILENAME,FNR;next}{print NR,FILENAME,FNR}' passwd shadow
1 passwd 1
2 passwd 2
3 passwd 3
4 shadow 1
5 shadow 2
6 shadow 3
# echo '' | awk '{aa[1]=1;aa[2]=2;aa["b"]="b";print aa[1]}'
1
# awk -F: '{for(i=1;i<=NF;i++){a[i]=$i;print a[1];break}}' passwd
root
bin
daemon
# cat c1
01 北京
02 上海
03 广州
04 深圳
# cat c2
01 beijing
02 shanghai
03 guangzhou
04 shenzhen
# awk 'NR==FNR{a[$1]=$0}NR!=FNR{print a[$1],$2}' c1 c2
01 北京 beijing
02 上海 shanghai
03 广州 guangzhou
04 深圳 shenzhen
sub
和gsub
函数:用来替换# cat test.txt
tom bob mary tom
bob tom mary tom
# awk '{sub("tom","TOM");print}' test.txt
TOM bob mary tom
bob TOM mary tom
但是只替换了每行的第一个,要想替换所有的,使用gsub
函数:
# awk '{gsub("tom","TOM");print}' test.txt
TOM bob mary TOM
bob TOM mary TOM
只替换最后一个:
# awk '{gsub("tom","TOM",$NF);print}' test.txt
tom bob mary TOM
bob tom mary TOM
length
函数用来计算字符串长度# awk -F: '{print $1,length($1)}' passwd
root 4
bin 3
daemon 6
substr
函数用来截取一段数据# awk -F: '{print substr($1,2)}' passwd
oot
in
aemon
# awk -F: '{print substr($1,2,2)}' passwd
oo
in
ae
match
函数的使用,从字符串中匹配满足某种格式的位置# awk -F: '{xx=match($1,"oo");print xx}' passwd
2
0
0
match
中有两个变量,RSTART
可以获取到match
得到的值,RLENGTH
表示的是match
中满足格式的字符创的长度,一般会结合substr截取我们所需要的数据:
# awk -F: '{xx=match($1,"oo");print xx,RSTART,RLENGTH}' passwd
2 2 2
0 0 -1
0 0 -1
split
函数的使用:split("原始字符串",数组名,<分隔符>)# echo '' | awk '{split("2018-05-20",xx,"-");print xx[1],xx[2],xx[3]}'
2018 05 20
# awk '{split($0,xx,":");print xx[1]}' passwd
root
bin
daemon
getline
函数可以让我们使用操作系统命令,也可以读取另一个文件内容# awk '{getline;print $0}' c1
02 上海
04 深圳
可以看到getline
将本文件的下一行赋值给当前的$0
# awk '{getline xx;print $0,xx}' c1
01 北京 02 上海
03 广州 04 深圳
在这里将下一行的值赋值给了getline后的变量xx,并没有影响当前的$0
getline < file
:该用法读取file文件的一行赋值给当前的$0
getline xx < file
:该用法读取file文件的一行赋值给变量xx
# awk '{getline xx< "c2";print xx,$2}' c1
01 beijing 北京
02 shanghai 上海
03 guangzhou 广州
04 shenzhen 深圳
# echo '' | awk '{"date"|getline xx;print xx}'
2018年 05月 20日 星期日 19:57:04 CST
# echo '' | awk '{while("ls /boot"|getline xx) print xx}'
config-3.10.0-327.el7.x86_64
grub2
initramfs-0-rescue-ba7506401e184ceea617517542f6b7c7.img
initramfs-3.10.0-327.el7.x86_64.img
initrd-plymouth.img
symvers-3.10.0-327.el7.x86_64.gz
System.map-3.10.0-327.el7.x86_64
vmlinuz-0-rescue-ba7506401e184ceea617517542f6b7c7
vmlinuz-3.10.0-327.el7.x86_64
# echo '' | awk '{print "请输入:";getline name<"/dev/tty";print "你好:" name}'
请输入:
tom
你好:tom
printf
不带换行符# awk -F: '{printf "%-8s|%-5d\n",$1,$3}' passwd
root |0
bin |1
daemon |2
其中%-8s
中的-8表示显示宽度为8个字符,-
表示左对齐,+
号表示右对齐%d
显示的整数%f
显示的是浮点数,%10.5f
表示总共显示十位。小数点后显示5位%e
用科学计数法显示
# echo '' | awk '{printf "%d\n",3*3.5}'
10
# echo '' | awk '{printf "%f\n",3*3.5}'
10.500000
# echo '' | awk '{printf "%-.10f\n",10/3}'
3.3333333333
# echo '' | awk '{printf "%e\n",3*3.5}'
1.050000e+01