awk应用案例

  • awk并非一个简单命令,它其实是一门编程语言,适合处理文本类数据,awk处理文本是以记录(文本中的行)为单位的,它会遍历文件的每条记录并进行处理

  • awk语法格式: awk 'Pattern {Action}' filename

  • awk工作原理:awk读取一条记录,并将记录赋值给内部变量$0,记录被分隔符分割成多个字段,每个字段存储到指定编号的变量中,从$1开始,(awk内部变量FS用来指定字段分隔符,默认为空格,包含制表符和空格符,也可用-F来自定义分隔符),对于每一条记录,按照给定的Pattern进行匹配,匹配成功则执行Action,匹配失败,则不执行Action,其中Pattern和Action都是可选的,但必须提供其中一个,如果未指定Pattern,则对所有输入行都执行Action,如果未指定Action,则输出匹配行的内容

  • $0 表示文本中的一条记录即一行内容

[root@linux01 ~]#  head -n5 /etc/passwd| awk '{print $0}'
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
  • 截取其中若干列内容,awk默认空格为分隔符,如果要改变分隔符可用"-F"选项自定义
[root@linux01 ~]#  head -n5 /etc/passwd| awk -F: '{print $1,$3}'
root 0
bin 1
daemon 2
adm 3
lp 4
  • 添加修饰内容,BEGIN和END是awk关键字,前者表示在awk对文件处理前首先被执行,后者表示在awk对文件处理后被执行
[root@linux01 ~]#  head -n5 /etc/passwd| awk -F: 'BEGIN {print "=====begin====="} {print $1,$3} END {print "=====end====="}'
=====begin=====
root 0
bin 1
daemon 2
adm 3
lp 4
=====end=====
  • awk可实现数学计算,$3+$4表示以:(冒号)分隔的第三和第四列之和
[root@linux01 ~]#  head -n5 /etc/passwd| awk -F: 'BEGIN {print "=====begin====="} {print $1,$2,$3+$4} END {print "=====end====="}'
=====begin=====
root x 0
bin x 2
daemon x 4
adm x 7
lp x 11
=====end=====
  • 针对输出结果进行过滤,~表示匹配意思
[root@linux01 ~]#  head -n5 /etc/passwd| awk -F: 'BEGIN {print "=====begin====="} $1~/lp/ {print $1,$2,$3+$4} END {print "=====end====="}'
=====begin=====
lp x 11
=====end=====
  • 巧妙显示奇/偶行,'NR%2==0 {next}'作为awk的Pattern匹配部分,其中NR表示当前记录的行号,整句表示NR被整除为TRUE的话,则执行next动作,而遇到next,就会忽略后面的Action部分,直接进入下一行处理,所以显示为奇数行输出
[root@linux01 ~]# cat -n file1.txt    
     1	test 1
     2	one two three proce
     3	test 2 
     4	test test file
[root@linux01 ~]# awk 'NR%2==0 {next} {print NR,$0}' file1.txt 
1 test 1
3 test 2 
[root@linux01 ~]# awk 'NR%2!=0 {next} {print NR,$0}' file1.txt 
2 one two three proce
4 test test file
  • 固定行合并,通过第一部分和第二部分语句,可以实现3行一组的前两行内容存放到变量T中,当处理第三行时,就会把所有之前存储的内容都输出来,然后清空变量T,再进行下一组数据处理
[root@linux01 ~]# cat -n file1.txt 
     1	test 1
     2	one two three proce
     3	test 2 
     4	test test file
     5	wsr239wfgrte
     6	fw9efs0art4
     7	4tesd8w40-sdgd
     8	43q8wsfjwrskdpoht
     9	rear8ut9spgoog
    10	45678
    11	s34otjvsa;
    12	4su02q9jdgopro[
[root@linux01 ~]# awk 'NR%3!=0 {T=(T" "$0);next} {print T,$0;T=""}' file1.txt 
 test 1 one two three proce test 2 
 test test file wsr239wfgrte fw9efs0art4
 4tesd8w40-sdgd 43q8wsfjwrskdpoht rear8ut9spgoog
 45678 s34otjvsa; 4su02q9jdgopro[
  • 以上案例存在一个小问题,文件行数刚好是3的倍数,如果不是3的倍数呢...
[root@linux01 ~]# cat -n file1.txt 
     1	test 1
     2	one two three proce
     3	test 2 
     4	test test file
     5	wsr239wfgrte
     6	fw9efs0art4
     7	4tesd8w40-sdgd
     8	43q8wsfjwrskdpoht
     9	rear8ut9spgoog
    10	45678
    11	s34otjvsa;
[root@linux01 ~]# awk 'NR%3!=0 {T=(T" "$0);next} {print T,$0;T=""}' file1.txt     -->发现丢失了10,11行内容
 test 1 one two three proce test 2 
 test test file wsr239wfgrte fw9efs0art4
 4tesd8w40-sdgd 43q8wsfjwrskdpoht rear8ut9spgoog

[root@linux01 ~]# awk 'NR%3!=0 {T=(T" "$0);next} {print T,$0;T=""} END {print T}' file1.txt     -->改进后命令,因为文本行数不是3的倍数时,不是3的倍数的最后一组记录存放在变量T中,只要我们把它输出即可完美解决此问题
 test 1 one two three proce test 2 
 test test file wsr239wfgrte fw9efs0art4
 4tesd8w40-sdgd 43q8wsfjwrskdpoht rear8ut9spgoog
 45678 s34otjvsa;
  • 不定行数合并,在于用正则来找规律分组
[root@linux01 ~]# cat -n file1.txt 
     1	test 1
     2	one two three proce
     3	test 2
     4	abc bca file
     5	wsr239wfgrte
     6	fw9efs0art4
     7	4tesd8w40-sdgd
     8	test 3
     9	43q8wsfjwrskdpoht
    10	rear8ut9spgoog
    11	45678
    12	s34otjvsa;
    13	test 4
[root@linux01 ~]# awk 'BEGIN {T=""} /test/ {print T;T=$0;next} {T=T" "$0} END {print T}' file1.txt 

test 1 one two three proce
test 2 abc bca file wsr239wfgrte fw9efs0art4 4tesd8w40-sdgd
test 3 43q8wsfjwrskdpoht rear8ut9spgoog 45678 s34otjvsa;
test 4
  • 合并所有行
[root@linux01 ~]# cat -n file1.txt 
     1	test 1
     2	one two three proce
     3	test 2
     4	abc bca file
     5	wsr239wfgrte
     6	fw9efs0art4
     7	4tesd8w40-sdgd
     8	test 3
     9	43q8wsfjwrskdpoht
    10	rear8ut9spgoog
    11	45678
    12	s34otjvsa;
    13	test 4
[root@linux01 ~]# awk '{T=T" "$0} END {print T}' file1.txt 
 test 1 one two three proce test 2 abc bca file wsr239wfgrte fw9efs0art4 4tesd8w40-sdgd test 3 43q8wsfjwrskdpoht rear8ut9spgoog 45678 s34otjvsa; test 4

** 输出多个文件内容

[root@linux01 ~]# cat new.txt 
root:x:0:0:root:/root:/bin/bash
this is a file
good ha 
feng file
[root@linux01 ~]# cat test.txt 
/0/,/3/p
sfwefov
w342re0k
fewfklfs;ar
sfd9wo4sd
[root@linux01 ~]# awk '{print $0}' new.txt test.txt 
root:x:0:0:root:/root:/bin/bash
this is a file
good ha 
feng file
/0/,/3/p
sfwefov
w342re0k
fewfklfs;ar
sfd9wo4sd

** 输出第一个文件的第一行和第二个文件的第二行内容,NR表示已读记录数,不论有几个文件,每读一条记录,值就会加1,FNR表示当前文件已读记录数,每读取一条记录,值就会加1,但更换文件后,该变量会重新从零开始,NR==FNR时,表示awk正在处理第一个文件,NR>FNR时,表示正在处理第二个文件,FILENAME表示当前处理文件的文件名

[root@linux01 ~]# cat new.txt 
root:x:0:0:root:/root:/bin/bash
this is a file
good ha 
feng file
[root@linux01 ~]# cat test.txt 
/0/,/3/p
sfwefov
w342re0k
fewfklfs;ar
sfd9wo4sd
[root@linux01 ~]# awk 'NR==FNR&&FNR==1 {print FILENAME,$0} NR>FNR&&FNR==2 {print FILENAME,$0}' n
ew.txt test.txt 
new.txt root:x:0:0:root:/root:/bin/bash
test.txt sfwefov

** 上面的案例在处理两个以上文件时有所局限,我们可以转变一下思路,引入一个环境变量,叫做ARGIND,用来指示当前处理的文件编号

[root@linux01 ~]# cat new.txt 
root:x:0:0:root:/root:/bin/bash
this is a file
good ha 
feng file
[root@linux01 ~]# cat test.txt 
/0/,/3/p
sfwefov
w342re0k
fewfklfs;ar
sfd9wo4sd
[root@linux01 ~]# cat file1.txt 
test 1
one two three proce
test 2
abc bca file
wsr239wfgrte
fw9efs0art4
4tesd8w40-sdgd
test 3
43q8wsfjwrskdpoht
rear8ut9spgoog
45678
s34otjvsa;
test 4
[root@linux01 ~]# awk 'ARGIND==1&&FNR==1 {print FILENAME,$0} ARGIND==2&&FNR==2 {print FILENAME,$0} ARGIND==3&&FNR==3 {print FILENAME,$0}' new.txt test.txt file1.txt 
new.txt root:x:0:0:root:/root:/bin/bash
test.txt sfwefov
file1.txt test 2

** 上面案例还可以用类似数组方式处理,ARGV表示命令行参数的数组

[root@linux01 ~]# cat new.txt 
root:x:0:0:root:/root:/bin/bash
this is a file
good ha 
feng file
[root@linux01 ~]# cat test.txt 
/0/,/3/p
sfwefov
w342re0k
fewfklfs;ar
sfd9wo4sd
[root@linux01 ~]# cat file1.txt 
test 1
one two three proce
test 2
abc bca file
wsr239wfgrte
fw9efs0art4
4tesd8w40-sdgd
test 3
43q8wsfjwrskdpoht
rear8ut9spgoog
45678
s34otjvsa;
test 4
[root@linux01 ~]# awk 'FILENAME==ARGV[1]&&FNR==1 {print FILENAME,$0} FILENAME==ARGV[2]&&FNR==2 {print FILENAME,$0} FILENAME==ARGV[3]&&FNR==3 {print FILENAME,$0}' new.txt test.txt file1.txt 
new.txt root:x:0:0:root:/root:/bin/bash
test.txt sfwefov
file1.txt test 2

** 我们想用/etc/shadow文件中的加密字段替换/etc/passwd文件中的x密码字段,OFS表示输出时指定的分隔符,FS表示字段分隔符,读取shadow文件时,把加密字段内容放入下标为用户列的数组中,再读取passwd文件时,再赋值给x密码字段,输出修改后内容即可

[root@linux01 ~]# awk 'BEGIN {OFS=FS=":"} NR==FNR {a[$1]=$2} NR>FNR {$2=a[$1];print}' /etc/shadow /etc/passwd
root:$6$CYXcNKe6hd7jzBgx$cA8WUgq/mYEymotuD0YAJRBkYtgc5nc8MFfPokHjC0LrfmHGtSIx3zE0YS5ML2Dc2YaG8Kl7khssL0faik7AS.:0:0:root:/root:/bin/bash
bin:*:1:1:bin:/bin:/sbin/nologin
daemon:*:2:2:daemon:/sbin:/sbin/nologin
adm:*:3:4:adm:/var/adm:/sbin/nologin
lp:*:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:*:5:0:sync:/sbin:/bin/sync
shutdown:*:6:0:shutdown:/sbin:/sbin/shutdown
halt:*:7:0:halt:/sbin:/sbin/halt
mail:*:8:12:mail:/var/spool/mail:/sbin/nologin
operator:*:11:0:operator:/root:/sbin/nologin
games:*:12:100:games:/usr/games:/sbin/nologin
ftp:*:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:*:99:99:Nobody:/:/sbin/nologin
systemd-bus-proxy:!!:999:997:systemd Bus Proxy:/:/sbin/nologin
systemd-network:!!:192:192:systemd Network Management:/:/sbin/nologin
dbus:!!:81:81:System message bus:/:/sbin/nologin
polkitd:!!:998:996:User for polkitd:/:/sbin/nologin
tss:!!:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin
postfix:!!:89:89::/var/spool/postfix:/sbin/nologin
sshd:!!:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
chrony:!!:997:995::/var/lib/chrony:/sbin/nologin
jenkins:!!:996:994:Jenkins Automation Server:/var/lib/jenkins:/bin/false
gitlab-www:!!:995:992::/var/opt/gitlab/nginx:/bin/false
git:!!:994:991::/var/opt/gitlab:/bin/sh
gitlab-redis:!!:993:990::/var/opt/gitlab/redis:/bin/false
gitlab-psql:!!:992:989::/var/opt/gitlab/postgresql:/bin/sh
gitlab-prometheus:!!:991:988::/var/opt/gitlab/prometheus:/bin/sh
virftp:!!:1000:1000::/home/virftp:/sbin/nologin

** getline操作文件案例,你会觉得getline命令也可以实现奇偶行输出功能,但不建议这样用,因为有缺憾,只有在处理文本总数是偶数行时适用,在awk中,当getline左右没有"<" "|"时,getline是对当前打开文件操作,表示读取当前行的下一行内容,并把数据赋值给$0,同时更新NF,NR,FNR

[root@linux01 ~]# seq 10|awk '{getline;print}'
2
4
6
8
10
[root@linux01 ~]# seq 10|awk '{print;getline}'
1
3
5
7
9
[root@linux01 ~]# seq 10|awk '{print;getline;print}'
1
2
3
4
5
6
7
8
9
10
[root@linux01 ~]# seq 11|awk '{getline;print}'    -->如果换成奇数行内容getline命令就会出错了
2
4
6
8
10
11

** 在awk中,当getline左右有重定向符"< 、<<"或"|"时,getline作用于其后的输入文件而不是当前打开的文件,由于输入文件是新打开的,并没有被awk读取,因此getline返回的是该文件的第一行,而不是当前读取文件的隔行内容,getline的返回值: 1:表示正常读取一行数据;0:表示到了文件末尾;-1:表示读取遇到错误;因此案例中当处理test文件第一行内容时,getline会读取文件num的内容,直至文件指针指向文件末尾,以后每处理一行文件test的数据,getline返回值都是0,所以不会再有文件num的内容输出,getline重定向文件时,后面必须跟字符串类型,因此文件名一定要加双引号

[root@linux01 ~]# cat test.txt 
1 /sdfa/,/ewra/p
2 sfwefov
3 wsfresdfwek
4 fewfklfs;ar
5 sfdsfawowesd
[root@linux01 ~]# cat num.txt 
10 20 30
40 50 60
[root@linux01 ~]# awk '' test.txt 
[root@linux01 ~]# awk '{print $0;while((getline < "num.txt") > 0) print $0}' test.txt 
1 /sdfa/,/ewra/p
10 20 30
40 50 60
2 sfwefov
3 wsfresdfwek
4 fewfklfs;ar
5 sfdsfawowesd

** 用system调用shell,awk通过system()函数来调用shell的程序

[root@linux01 ~]# awk 'BEGIN {system("ls -al")}'
总用量 136
dr-xr-x---.  8 root root  4096 12月 11 15:13 .
dr-xr-xr-x. 18 root root   256 11月 23 21:53 ..
-rw-r-----   1 root root  1502 11月 24 21:29 1.log
-rw-r--r--   1 root root   161 11月 20 10:38 adduser.sh
-rw-------.  1 root root  1422 11月 14 18:11 anaconda-ks.cfg
-rw-------.  1 root root 18728 12月 11 11:56 .bash_history
-rw-r--r--.  1 root root    18 12月 29 2013 .bash_logout
-rw-r--r--.  1 root root   176 12月 29 2013 .bash_profile
-rw-r--r--.  1 root root   176 12月 29 2013 .bashrc
drwxr-xr-x   3 root root    18 11月 28 23:08 .config
-rw-r--r--.  1 root root   100 12月 29 2013 .cshrc
-rw-r--r--   1 root root    91 11月 20 10:33 deluser.sh
-rw-r--r--   1 root root   151 12月 10 12:12 file1.txt
-rw-r--r--   1 root root    56 11月 24 01:08 .gitconfig
-rw-r--r--   1 root root  4360 11月 22 15:11 :ii:wq
-rw-r--r--   1 root root    20 12月 11 10:59 k1.txt
-rw-------   1 root root    54 11月 23 01:29 .lesshst
drwxr-xr-x   3 root root    19 11月 28 23:08 .local
drwxr-x---   2 root root    19 11月 24 21:28 logs
-rw-r--r--   1 root root    66 12月 10 21:33 new.txt
-rw-r--r--   1 root root    18 12月 11 15:13 num.txt
-rw-r--r--   1 root root  1494 11月 23 17:38 passwd
-rw-r--r--   1 root root 12288 11月 27 14:54 .passwd.swp
drwxr-----   3 root root    19 11月 20 20:47 .pki
-rw-------   1 root root  1024 11月 29 00:45 .rnd
drwxr-xr-x   3 root root    32 11月 23 21:38 sample
drwx------   2 root root    82 11月 23 16:34 .ssh
-rw-r--r--.  1 root root   129 12月 29 2013 .tcshrc
-rw-r--r--   1 root root    43 11月 22 11:27 tee
-rw-r--r--   1 root root    70 12月 11 11:40 test.txt
-rw-------   1 root root  5627 12月 10 12:12 .viminfo
-rw-------   1 root root  4286 11月 27 18:01 .viminfo.tmp

转载于:https://my.oschina.net/u/3119180/blog/1587441

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值