awk,命名于三大作者的名字首字母,也是一门编程语言,这里介绍的是文本处理工具
一,awk原理
[root@m01~]# head -10 /etc/passwd | awk -F: 'BEGIN{count=0}{print$0,count++}END{print count}'
root:x:0:0:root:/root:/bin/bash 0
bin:x:1:1:bin:/bin:/sbin/nologin 1
daemon:x:2:2:daemon:/sbin:/sbin/nologin 2
adm:x:3:4:adm:/var/adm:/sbin/nologin 3
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin 4
sync:x:5:0:sync:/sbin:/bin/sync 5
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown 6
halt:x:7:0:halt:/sbin:/sbin/halt 7
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin 8
operator:x:11:0:operator:/root:/sbin/nologin 9
10
等同于
[root@m01~]# head -10 /etc/passwd > awk.txt
[root@m01~]# awk 'BEGIN{count=0;FS=":"}{print $o,count++}END{print count}' awk.txt
awk -F: 'BEGIN{count=0}{print$0,count++}END{print count}'
BEGIN{count=0} 行前处理
{print$0,count++} 行处理
END{print count} 行后处理
运行原理:
1,先运行 行前处理 BEGIN{count=0}
2,再运行 行处理
- 读一行到内存中,然后awk 生成一堆内置变量
&0 : 一整行内容
&1 : 以指定分隔符隔开的第一段内容
&0 : 以指定分隔符隔开的第二段内容
NR 行号
NF 列,即段
FS 行的分隔符 FS=" ",把输入的内容以空格为分隔符
OFS 以...为分隔符 例OFS=":" ,把输出的内容以:为分隔符
- 运行行处理代码
- 循环往复 直到所有行被处理
3,最后运行 行后处理
# 取出最后一行
[root@m01~]# awk 'BEGIN{count=0;FS=":"}{print $NF}' awk.txt
/bin/bash
/sbin/nologin
/sbin/nologin
/sbin/nologin
/sbin/nologin
/bin/sync
/sbin/shutdown
/sbin/halt
/sbin/nologin
/sbin/nologin
# 标记行号
[root@m01~]# awk -F: '{print NR,$0}' awk.txt
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
6 sync:x:5:0:sync:/sbin:/bin/sync
7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
8 halt:x:7:0:halt:/sbin:/sbin/halt
9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
10 operator:x:11:0:operator:/root:/sbin/nologin
#取出第一列和第三列,以-为分隔符
[root@m01~]# awk 'BEGIN{count=0;FS=":";OFS="-"}{print $1,$3}' awk.txt
root-0
bin-1
daemon-2
adm-3
lp-4
sync-5
shutdown-6
halt-7
mail-8
operator-11
# 以空格为分隔符,直接逗号隔开即可;如下
[root@m01~]# awk -F: 'BEGIN{count=0}{print $1,$3;count++}END{print count}' awk.txt
#取出第一行和倒数第二行
[root@m01~]# awk -F: '{print $1,$(NF-1)}' awk.txt
root /root
bin /bin
daemon /sbin
adm /var/adm
lp /var/spool/lpd
sync /sbin
shutdown /sbin
halt /sbin
mail /var/spool/mail
operator /root
# 注意 使用NF倒着数,一定要加上括号
$(NF-1) 倒数第一段
$(NF-2) 倒数第二段
#可以把行处理放在一个文件中
[root@m01~]# cat a.awk
{print $1,$NF}
[root@m01~]# awk -F: -f a.awk awk.txt
root /bin/bash
bin /sbin/nologin
daemon /sbin/nologin
adm /sbin/nologin
lp /sbin/nologin
sync /bin/sync
shutdown /sbin/shutdown
halt /sbin/halt
mail /sbin/nologin
operator /sbin/nologin
# 注意 这时候不需要再加 引号
二,模式匹配
1,行定位
# 打印出所有行的第一列
[root@m01~]# awk -F: '{print NR,$1}' awk.txt
1 root
2 bin
3 daemon
4 adm
5 lp
6 sync
7 shutdown
8 halt
9 mail
10 operator
# 打印第三行的第一列
[root@m01~]# awk -F: 'NR == 3{print $1}' awk.txt
daemon
# 取出 第一行到第三行的第一列和最后一列
[root@m01~]# awk -F: 'NF >=1 && NR <=3{print $1,$NF}' awk.txt
root /bin/bash
bin /sbin/nologin
daemon /sbin/nologin
# 取出第一行和第三行的 第一列和第三列
[root@m01~]# awk -F: 'NR == 1 || NR == 3{print $1,$3}' awk.txt
root 0
daemon 2
# 打印出包含root的行
[root@m01~]# awk -F: '/root/{print NR,$0}' awk.txt
1 root:x:0:0:root:/root:/bin/bash
10 operator:x:11:0:operator:/root:/sbin/nologin
# 打印出 第一列中包含root的行
[root@m01~]# awk -F: '$1 ~ /root/{print NR,$0}' awk.txt
1 root:x:0:0:root:/root:/bin/bash
# 打印出用户名(即第一列)中不以root开头的行
[root@m01~]# awk -F: '$1 !~ /root/{print NR,$0}' awk.txt
等同于
[root@m01~]# awk -F: '!($1 ~ /^root/){print NR,$0}' awk.txt
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
... ...
# 注意感叹号前需要用上小括号 ()
# 打印出不是大于等于第二行的内容,即第一行
[root@m01~]# awk -F: '! (NR>=2){print NR,$0}' awk.txt
1 root:x:0:0:root:/root:/bin/bash
# 取出 从zx 到xy的行
[root@m01~]# cat a.txt
1111zx
2345
xy
23
345
zx
1213
123xy
[root@m01~]# awk -F: '/zx/,/xy/{print NR,$0}' a.txt
1 1111zx
2 2345
3 xy
6 zx
7 1213
8 123xy
# 注意会有两段内容,从匹配zx开始,到xy结束为一段
# 如上,如果第二段没有xy作为结束,会一直吃到结尾
[root@m01~]# cat a.txt
1111zx
2345
xy
23
345
zx
1213
[root@m01~]# awk -F: '/zx/,/xy/{print NR,$0}' a.txt
1 1111zx
2 2345
3 xy
6 zx
7 1213
# 取出2-4行中包含xy的内容
[root@m01~]# awk -F: 'NR >=2 && NR <= 4 && /xy/{print NR,$0}' a.txt
3 xy
2.算数运算
如下的意思:姓名zx的月薪是12000 ,年龄18岁,取出年薪超过10w的人
[root@m01~]# cat b.txt
zx:12000:18
egon:21000:38
lil:3000:19
[root@m01~]# awk -F: '$2*12 > 100000{print $1}' b.txt
zx
egon
# 打印文件中的偶数行
[root@m01~]# awk -F: 'NR % 2 == 0{print NR,$0}' awk.txt
2 bin:x:1:1:bin:/bin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
6 sync:x:5:0:sync:/sbin:/bin/sync
8 halt:x:7:0:halt:/sbin:/sbin/halt
10 operator:x:11:0:operator:/root:/sbin/nologin
# 打印文件中的偶数行的用户
[root@m01~]# awk -F: 'NR % 2 == 0{print NR,$1}' awk.txt
2 bin
4 adm
6 sync
8 halt
10 operator
# 打印文件中的基数行
[root@m01~]# awk -F: 'NR % 2 !=0{print NR,$0}' awk.txt
1 root:x:0:0:root:/root:/bin/bash
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
等同于
[root@m01~]# awk -F: 'NR % 2 ==1{print NR,$0}' awk.txt
% 取余数
3,
# 取出第一列,并标注行号
1~]# awk -F: 'BEGIN{OFS="---"}{print "行号",NR,"用户名",$1}' awk.txt
行号---1---用户名---root
行号---2---用户名---bin
行号---3---用户名---daemon
行号---4---用户名---adm
行号---5---用户名---lp
行号---6---用户名---sync
行号---7---用户名---shutdown
行号---8---用户名---halt
行号---9---用户名---mail
行号---10---用户名---operator
... ...
为方便看的清晰,可以调整
方式一:
[root@m01~]# awk -F: 'BEGIN{OFS="---"}{print "行号"NR,"用户名"$1}' awk.txt
行号1---用户名root
行号2---用户名bin
行号3---用户名daemon
行号4---用户名adm
行号5---用户名lp
行号6---用户名sync
行号7---用户名shutdown
行号8---用户名halt
行号9---用户名mail
行号10---用户名operator
方式二:
[root@m01~]# awk -F: '{printf "行号:%s---用户名:%s\n",NR,$1}' awk.txt
行号:1---用户名:root
行号:2---用户名:bin
行号:3---用户名:daemon
行号:4---用户名:adm
行号:5---用户名:lp
行号:6---用户名:sync
行号:7---用户名:shutdown
行号:8---用户名:halt
行号:9---用户名:mail
行号:10---用户名:operator
# 注意,printf ; \n 换行符
4,awk 之 if 判断
# 算出管理员用户数为 系统用户数为 普通用户数为各为多少(了解即可)
[root@m01~]# awk -F: 'BEGIN{x=0;y=0;z=0}{if ($3==0){x++} else if ($3>=3 && $3<=999){y++} else {z++}}END{printf "管理员用户数为:%s 系统用户数为:%s 普通用户数为:%s\n",x,y,z}' /etc/passwd
管理员用户数为:1 系统用户数为:23 普通用户数为:3
x 代表管理员账户数;y 代表系统用户数;z 代表普通用户数
shell脚本
[root@m01~]# cat test.sh
#!/bin/bash
x=0
y=0
z=0
for uid in $(awk -F: '{print $3}' /etc/passwd)
do
if [ $uid -eq 0 ];then
let x++
elif [ $uid -ge 1 -a $uid -le 999 ];then
let y++
else
let z++
fi
done
printf "管理员数为:%s 系统用户数为:%s 普通用户数为:%s\n" $x $y $z
[root@m01~]# ./test.sh
管理员数为:1 系统用户数为:25 普通用户数为:1
# 注意:
shell脚本里需要用$ q取值并中间用空格隔开;$x $y $z
5,awk 之 while循环
# 循环三次
方式一:
[root@m01~]# cat test.txt
1234abc
5678qqq
3456
pppp
[root@m01~]# awk '{n=1;while (n<=3){print $0;n++}}' test.txt
1234abc
1234abc
1234abc
5678qqq
5678qqq
5678qqq
3456
3456
3456
pppp
pppp
pppp
方式二:
[root@m01~]# awk '{for (n=1;n<=3;n++){print $0}}' test.txt
1234abc
1234abc
1234abc
... ...
5,awk 之 数组功能
# 所有的用户名 存到数组里(了解即可)
方式一:
[root@m01~]# awk -F: 'BEGIN{i=0}{username[i]=$1;i++}END{for (x=0;x<i;x++){print username[x]}}' /etc/passwd
root
bin
daemon
adm
lp
sync
shutdown
halt
mail
... ...
方式二:
[root@m01~]# awk -F: 'BEGIN{i=0}{username[i]=$1;i++}END{for (x in username) {print username[x]}}' /etc/passwd
练习题补充
# 打印出用户名字符为4个字符的
[root@m01~]# awk -F: '$1 ~ /^....$/{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
sync:x:5:0:sync:/sbin:/bin/sync
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
网友评论