三剑客ShellLinux

Linux文本编辑三剑客之---awk的使用

2021-08-27  本文已影响0人  莫讠

1、awk

1.1 认识awk

awk是一种编程语言,用于在linux/unix下对文本和数据进行处理。数据可以来自标准输入(stdin)、一个或多个文件,或其它命令的输出。它支持用户自定义函数和动态正则表达式等先进功能,是linux/unix下的一个强大编程工具。它在命令行中使用,但更多是作为脚本来使用。awk有很多内建的功能,比如数组、函数等,这是它和C语言的相同之处,灵活性是awk最大的优势。

awk其实不仅仅是工具软件,还是一种编程语言。不过,本文只介绍它的命令行用法,对于大多数场合,应该足够用了。

1.2 使用awk

1.2.1 语法

awk [options] 'program' var=value file``…

awk [options] -f programfile var=value file``…

awk [options] 'BEGIN{ action;… } pattern{ action;… } END{ action;… }' file ...

1.2.2 常用命令选项

1.3 awk变量

变量:内置和自定义变量,每个变量前加 -v 命令选项

1.3.1 内置变量

(1)格式

$ cat awkdemo
hello:world
linux:redhat:lalala:hahaha
kevin:love:youou
$ awk -v FS=":" '{print $1,$2}' awkdemo 
hello world
linux redhat
kevin love

当然也可以写成另外一种形式:

$ awk -F: '{print $1,$2}' awkdemo 
hello world
linux redhat
kevin love

另外通过awk命令可以指定自己所需要的字符,比如e:

awk -v FS="e:" '{print $1,$2}' awkdemo 
hello:world 
linux:redhat:lalala:hahaha 
kevin:lov youou

但是如果用cut命令的话会导致报错,因为通过cut指定的分割符只能是单个字符,但是awk可以指定多个字符作为分割符

$ cut -d "e:" -f1,2 awkdemo 
cut: the delimiter must be a single character
Try 'cut --help' for more information.
awk -v FS=':' -v OFS='---' '{print $1,$2}' awkdemo 

这样便通过awk的方法将文本中的分隔符变成---

hello---world
linux---redhat
kevin---love
$ awk -F: {'print NF'} awkdemo 
2
4
3
$ awk '{print NR}' awkdemo awkdemo 
1
2
3
4
5
6
$ awk '{print FNR}' awkdemo awkdemo 
1
2
3
1
2
3
$ awk END'{print NR}' awkdemo awkdemo 
6
$ awk "{print FILENAME}" awkdemo demo
awkdemo
awkdemo
awkdemo
demo
demo
demo
demo

1.3.2 自定义变量

自定义变量( 区分字符大小写)

(1)-v var=value

① 先定义变量,后执行动作print

相当于在我的当前的文件的前面可以添加任何我想要的字段

$ awk -v name="kevin" -F: '{print name":"$0}' awkdemo 
kevin:hello:world
kevin:linux:redhat:lalala:hahaha
kevin:kevin:love:youou

② 在执行动作print后定义变量

$ awk -F: '{print name":"$0, name="kevin"}' awkdemo 
:hello:world kevin
kevin:linux:redhat:lalala:hahaha kevin
kevin:kevin:love:youou kevin

(2)在program 中直接定义

可以把执行的动作放在脚本中,直接调用脚本 -f

$ awk -F: -f awk.txt awkdemo 
kevin hello
kevin linux
kevin kevin

1.4 printf命令

比print更强大

1.4.1 格式

(1)格式化输出

printf "FORMAT"``, item1,item2, ...

① 必须指定FORMAT

不会自动换行,需要显式给出换行控制符,\n

③ FORMAT 中需要分别为后面每个item 指定格式符

(2)格式符:与item 一一对应

(3)修饰符:放在%c[/d/e/f...]之间

1.4.2 演示

当使用print的时候:

$ awk -F: '{print $1,$3}' /etc/passwd | head -n 2
root 0
bin 1
$ awk -F: '{printf "%20s---%u\n",$1,$3}' /etc/passwd | head -n 2
                root---0
                 bin---1
$ awk -F: '{printf "%-20s---%-15.3f\n",$1,$3}' /etc/passwd | head -n 2
root                ---0.000          
bin                 ---1.000  

1.5 操作符

1.5.1 格式

1.5.2 演示

(1)模式匹配符

$ df -h |awk -F: '$0 ~ /^\/dev/'
/dev/mapper/cl-root                   165G   81G   84G  50% /
/dev/sdb1                              37T   30T  7.0T  81% /GP
/dev/sda1                             197M  155M   43M  79% /boot
/dev/mapper/cl-var                     50G  1.8G   49G   4% /var
/dev/sdc1                             3.7T  2.9T  788G  79% /media/sdisk/usb2
/dev/sdd1                             1.9T  1.2T  728G  61% /media/sdisk/usb3

这种操作模式有些类似于

$ df -h |awk -F: '$0' | grep '/dev/'
tmpfs                                 126G   21G  106G  17% /dev/shm
/dev/mapper/cl-root                   165G   81G   84G  50% /
/dev/sdb1                              37T   30T  7.0T  81% /GP
/dev/sda1                             197M  155M   43M  79% /boot
/dev/mapper/cl-var                     50G  1.8G   49G   4% /var
/dev/sdc1                             3.7T  2.9T  788G  79% /media/sdisk/usb2
/dev/sdd1                             1.9T  1.2T  728G  61% /media/sdisk/usb3

结合之前提到的NF :字段数量,共有多少字段, NF引用最后一列,(NF-1)引用倒数第2列

只显示磁盘使用状况和磁盘名

$ df -h | awk '$0 ~ /^\/dev/ {print $(NF-1)"---"$1}'
50%---/dev/mapper/cl-root
81%---/dev/sdb1
79%---/dev/sda1
4%---/dev/mapper/cl-var
79%---/dev/sdc1
61%---/dev/sdd1

找出磁盘占比大于40%的
相当于在之前的命令的基础上添加了应该对第一行的筛选

$ df -h | awk '$0 ~ /^\/dev/ {print $(NF-1)"---"$1}' |awk -F%  '$1>40'
50%---/dev/mapper/cl-root
81%---/dev/sdb1
79%---/dev/sda1
79%---/dev/sdc1
61%---/dev/sdd1

(2)逻辑操作符

$ awk -F: '$3 >=6 && $3<=66 {print $1,$3}' /etc/passwd 
shutdown 6
halt 7
mail 8
uucp 10
operator 11
games 12
gopher 13
ftp 14
rpc 32
oprofile 16
rpcuser 29
gdm 42
ntp 38
apache 48
mailnull 47
smmsp 51
mysql 27
pegasus 66
postgres 26
$ awk -F: '$3==0 || $3>1000 {print $1,$3}' /etc/passwd 
root 0
nfsnobody 65534

(3)条件表达式(三目表达式)

$ awk -F: '{$3 >= 1000?usertype="common user":usertype="sysadmin user";print usertype,$1,$3}' /etc/passwd | head -n 30
sysadmin user root 0
sysadmin user bin 1
sysadmin user daemon 2
sysadmin user adm 3
sysadmin user lp 4
sysadmin user sync 5
sysadmin user shutdown 6
sysadmin user halt 7
sysadmin user mail 8
sysadmin user uucp 10
sysadmin user operator 11
sysadmin user games 12
sysadmin user gopher 13
sysadmin user ftp 14
sysadmin user nobody 99
sysadmin user dbus 81
sysadmin user usbmuxd 113
sysadmin user rpc 32
sysadmin user oprofile 16
sysadmin user vcsa 69
sysadmin user rtkit 499
sysadmin user abrt 173
sysadmin user hsqldb 96
sysadmin user avahi-autoipd 170
sysadmin user saslauth 498
sysadmin user postfix 89
sysadmin user rpcuser 29
common user nfsnobody 65534
sysadmin user haldaemon 68
sysadmin user gdm 42

1.6 awk PATTERN 匹配部分

1.6.1 格式

PATTERN:根据pattern 条件,过滤匹配的行,再做处理

(1)如果未指定:空模式,匹配每一行

(2)/regular expression/ :仅处理能够模式匹配到的行,正则,需要用/ / 括起来

(3)relational expression:关系表达式,结果为“真”才会被处理

真:结果为非0值,非空字符串

假:结果为空字符串或0值

(4)line ranges:行范围

startline(起始行),endline(结束行):/pat1/,/pat2/ 不支持直接给出数字,可以有多段,中间可以有间隔

(5)BEGIN/END 模式

BEGIN{}: 仅在开始处理文件中的文本之前执行一次

END{} :仅在文本处理完成之后执行

1.6.2 演示

$ awk -F: '{print $1,$2}' awkdemo 
hello world
linux redhat
kevin love
$ awk -F: '/kevin/{print $1,$2}' awkdemo 
kevin love

relational expression:关系表达式,结果为“真”才会被处理 ; 真:结果为非0值,非空字符串

$ awk -F: "1{print $1}" awkdemo 
hello:world
linux:redhat:lalala:hahaha
kevin:love:youou

结果为空字符串或0值时,以下代码则不会被执行

$ awk -F: "0{print $1}" awkdemo
$ awk -F: '/^h/,/^a/ {print $1}' awkdemo 
hello
linux
kevin

- BEGIN{}: 仅在开始处理文件中的文本之前执行一次

- END{} :仅在文本处理完成之后执行

$ awk -F: 'BEGIN{print "第一列"}{print $1} END{print "结束"}' awkdemo
第一列
hello
linux
kevin
结束

1.7 awk有意思的案例

$ seq 10
1
2
3
4
5
6
7
8
9
10

因为i=1为真,所以全部打印

$ seq 10 | awk 'i=1'
1
2
3
4
5
6
7
8
9
10

因为i=0为假,所以不打印

$ seq 10 | awk 'i=0'

只打印奇数行;奇数行i进入时本身为空,被赋为!i,即不为空,所以打印;偶数行i进入时本身不为空,被赋为!i,即为空,所以不打印

$ seq 10 | awk 'i=!i'
1
3
5
7
9

解释上一个操作,i在奇偶行的时候到底是一个什么样的值

$ seq 10 |awk '{i=!i;print i}'
1
0
1
0
1
0
1
0
1
0

当然你也可以选择只打印偶数行,相当于是上边打印奇数行的取反

$ seq 10 | awk '!(i=!i)'
2
4
6
8
10

当然为了打印出偶数行,我们也可以对i进行赋值,这样i在最开始的时候就不为空,刚好与打印奇数行的时候相反

$ seq 10 | awk -v 'i=1' 'i=!i'
2
4
6
8
10

1、awk高阶用法

1.1 awk控制语句—if-else判断

(1)语法

if (condition){statement;…} [else statement] 双分支

if (condition1){statement1} else if (condition2){statement2} else {statement3} 多分支

(2)使用场景:对awk 取得的整行或某个字段做条件判断

(3)演示

$ awk -F: '{if($3>10 && $3<20)print $1,$3}' /etc/passwd
operator 11
games 12
gopher 13
ftp 14
oprofile 16

打印出所有路径为/bin/bash的所有用户名以及路径

$ awk -F: '{if($NF=="/bin/bash") print $1,$NF}' /etc/passwd | head -n 10
root /bin/bash
chenys /bin/bash
liutao /bin/bash
git-admin /bin/bash
sysadmin /bin/bash
zhanglc /bin/bash
mysql /bin/bash
changlp /bin/bash
zhanghc /bin/bash
wangt /bin/bash

输出总例数大于3的行

$ awk -F: '{if(NF>2) print $0}' awkdemo
linux:redhat:lalala:hahaha
kevin:love:youou

第三列大于等于100的为Common user用户, 反之则为root or Sysuser用户

$ awk -F: '{if($3>=100) {printf "Common user: %s\n",$1} else{printf "root or Sysuser : %s\n",$1}}' 
/etc/passwd | head -n 20
root or Sysuser : root
root or Sysuser : bin
root or Sysuser : daemon
root or Sysuser : adm
root or Sysuser : lp
root or Sysuser : sync
root or Sysuser : shutdown
root or Sysuser : halt
root or Sysuser : mail
root or Sysuser : uucp
root or Sysuser : operator
root or Sysuser : games
root or Sysuser : gopher
root or Sysuser : ftp
root or Sysuser : nobody
root or Sysuser : dbus
Common user: usbmuxd
root or Sysuser : rpc
root or Sysuser : oprofile
root or Sysuser : vcsa

找到磁盘利用率超过40的设备名以及利用率

$ df -h | awk -F% '/^\/dev/ {print $1}' | awk '$NF>40{print $1,$NF}'
/dev/sda3 63
/dev/sdb2 74
/dev/sda2 69

做一个判断,当test>=90的时候为excellent,当60 < test <90的时候为pass,当test<60的时候为failed

$ awk 'BEGIN{test=100; if(test>=90){print "excellent"} else if(test>=60 && test <90){print "pass"} else{print "failed"}}'
excellent
$ awk 'BEGIN{test=55; if(test>=90){print "excellent"} else if(test>=60 && test <90){print "pass"} else{print "failed"}}'
failed
$ awk 'BEGIN{test=65; if(test>=90){print "excellent"} else if(test>=60 && test <90){print "pass"} else{print "failed"}}'
pass

1.2 awk控制语句—while循环

(1)语法

while``(condition){statement;…}

注:条件“真”,进入循环;条件“假”, 退出循环

(2)使用场景

对一行内的多个字段逐一类似处理时使用

对数组中的各元素逐一处理时使用

(3)演示

$ awk -F: '/^kevin/{i=1; while(i<=NF){print $i, length($i); i++}}' awkdemo
kevin 5
love 4
youou 5

为分隔,显示每一行的长度大于6的单词和其长度

$ awk -F: '{i=1;while(i<=NF) {if(length($i)>=6){print $i,length($i)}; i++}}' 
awkdemo
redhat 6
lalala 6
hahaha 6

计算从1加到100的和

$ awk 'BEGIN{i=1;sum=0;while(i<=100){sum=sum+i;i++} {print sum}}'
5050

1.3 awk控制语句—do-while循环

(1)语法

do {statement;…} while (condition)

意义:无论真假,至少执行一次循环体

(2)计算1+2+3+...+100=5050

$ awk 'BEGIN{i=1; sum=0; do{sum=sum+i;i++}while(i<=100) {print sum}}'
5050

1.4 awk控制语句—for循环

(1)语法

for``(expr1;expr2;expr3) {statement;…}

(2)特殊用法:遍历数组中的元素

for``(var in array) {``for``-body}

(3)演示

$ awk -F: '{for(i=1;i<=NF;i++){print $i,length($i)}}' awkdemo
hello 5
world 5
linux 5
redhat 6
lalala 6
hahaha 6
kevin 5
love 4
youou 5
$ cat sort.txt 
xiaoming m 90
xiaohong f 93
xiaohei m 80
xiaofang f 99

统计男m、女f 各自的平均分

$ awk '{m[$2]++; score[$2]+=$3} END{for(i in m){printf "%s:%6.2f\n",i,score[i]/m[i]}}' sort.txt 
m: 85.00
f: 96.00

1.5 数值\字符串处理

(1)数值处理

演示:

$ awk 'BEGIN{print rand()}'
0.237788

当加了srand()之后,就可以正常输出随机数了

$ awk 'BEGIN{srand();print rand()}'
0.625523
$ awk 'BEGIN{srand();print rand()}'
0.592696
$ awk 'BEGIN{srand(); print int(rand()*100%50)+1}'
21

(2)字符串处理:

演示:
将前面文本中的第一个:改变成-

$ echo "2008:08:08 08:08:08" | awk 'sub(/:/,"-",$1)'
2008-08:08 08:08:08
$ echo "2008:08:08 08:08:08" | awk 'gsub(/:/,"-",$0)'
2008-08-08 08-08-08
$ echo "2008:08:08 08:08:08" | awk '{split($0,i,":")} {for(n in i){print n,i[n]}}'
1 2008
2 08
3 08 08
4 08
5 08

1.8 awk中调用shell 命令

(1)system 命令

空格是awk 中的字符串连接符,如果system中需要使用awk中的变量可以使用空格分隔,或者说除了awk 的变量外其他一律用"" 引用 起来。

$ awk 'BEGIN{system("hostname")}'

R290-1.GenePlus
$ awk 'BEGIN{score=100; system("echo your score is " score)}'
your score is 100
上一篇下一篇

猜你喜欢

热点阅读