Linux三剑客之awk最佳实践

第1章 awk指哪打哪

笔者Q:972581034 交流群:605799367。有任何疑问可与笔者或加群交流

知识点:

  • 记录与字段
  • 模式匹配:模式与动作
  • 基本的awk执行过程
  • awk常用内置变量(预定义变量)
  • awk数组
  • awk语法:循环、条件
  • awk常用函数
  • 向awk传递参数
  • awk引用shell变量
  • awk小程序及调试思路

    [root@ctg ~]# awk --version|head -1
    GNU Awk 3.1.7
    

1.1 awk内置变量

FS 

输入字段(列)分隔符

-F :

相当于-vFS :

NR 

number of record 行号(记录号)

NF  

number of filed  每行有多少列

OFS

output 输出分隔符

RS 

record separator 记录分隔符每一的结束标记 默认是回车

IGNORECASE

是否忽略大小写  1为忽略

第1章 记录和字段

record记录==行, field字段相当于列,字段==列。
awk对每个要处理的输入数据认为都是具有格式和结构的,而不仅仅是一堆字符串。默认情况下,每一行内容都是一条记录,并以换行符分隔(n)结束

1.1.1 RS    记录分隔符   每一的结束标记默认是回车

[root@zeq files]# cat passwd.txt

root:x:0:0:root:/root:/bin/bash

bin:x:1:1:bin:/bin:/sbin/nologin     默认结束标记是回车,现在文件内容为两行

以/作为记录分隔符,显示行号和文件内容

[root@zeq files]# awk -vRS="/" '{print NR,$0}' passwd.txt   NR是显示行号   $0是显示一整行的内容

1 root:x:0:0:root:

2 root:

3 bin

4 bash

bin:x:1:1:bin:

5 bin:

6 sbin

7 nologin               现在以/为记录分隔符就变成了7行

这里的awk -vRS="/"     -v给变量赋值,RS是awk内置变量,设置/为记录分隔符

1.1记录分隔符-RS

1.2 awk中表示行和列

NR==1

第一行

$1

第一列

$NF

最后一列

$NF-1

倒数第二列

 

awk默认情况下每一行都是一个记录(record)

RS即 record
separator输入数据记录分隔符,每一行是怎么没的,表示每个记录输入的时候的分隔符,即行与行之间如何分隔。
NR即number of record记录(行)号,表示当前正在处理的记录(行)的号码
ORS即output record separator输出记录分隔符

awk使用内置变量RS来存放输入记录分隔符,RS表示的是输入的记录分隔符,这个值可以通过BEGIN模块重新定义修改。

1.3 awk模式匹配(条件)

模式-pattern  帮助你找到想要的行

1)正则表达式

2)比较

   >

   <

   ==

3)范围

4)BEGIN{} END{}

小结:

gawk
RS=行分隔符(记录分隔符)
ORS=输出记录分隔符
FS=字段分隔符(列分隔符)
OFS=输出字段分隔符
$0=整个记录
NF= 列数
NR= 行数
在{print ,}中,逗号的意思是OFS
BEGIN:主要用来定义内置变量.

1.4 正则表达式

~

某一列中包含xxx

!~

某一列中不包含xxx

^

以….开头的字符(列)

$

以….什么结尾的

.*

所有

^$

空行

转义字符 backslash  脱掉马甲打回原形

[]

匹配[]的每一个字符

+

一个字符出现1次或1次以上

|

或者

()

反向引用,一个整体,保护里面的内容

*

一个字符出现0次或者0次以上

{}

0{n,m} 数字0连续出现了至少n次,最多m次

?

一个字符出现0次或1次

1.2 对$0的认识

awk$0表示整行,其实awk使用$0来表示整条记录。记录分隔符

1.4.1 awk正则表达式练习

创建环境

mkdir -p /server/files/

cat >>/server/files/reg.txt<<EOF

Zhang Dandan    41117397   :250:100:175             第一列是姓氏

Zhang Xiaoyu    390320151  :155:90:201              第二列是名字

Meng  Feixue    80042789   :250:60:50               第一第二列合起来就是姓名

Wu    Waiwai    70271111   :250:80:75               第三列是对应的ID号码

Liu   Bingbing  41117483   :250:100:175             最后三列是三次捐款数量 

Wang  Xiaoai    3515064655 :50:95:135

Zi    Gege      1986787350 :250:168:200

Li    Youjiu    918391635  :175:75:300

Lao   Nanhai    918391635  :250:100:175

EOF

1.3 企业面试题

统计下面文件里单词出现的次数并排序
由sed -r ‘s#[^a-Z]+# #g’ /etc/passwd > /root/tongji.txt 创建
解答:

awk 'BEGIN{RS="[^a-zA-Z]"}{print $0}' tongji.txt |sort |uniq -c|sort -rn

1.4.2 取出第3列中以数字4开头的行

[root@zeq files]# awk '$3~/^4/' reg.txt    $3 第3列   ~ 包括    ^4 以4开头

Zhang Dandan    41117397   :250:100:175

Liu   Bingbing  41117483   :250:100:175

1.4 awk记录知识小结:

  1. NR(number of record)存放着每个记录的号(行号)读取新行时候会自动+1
  2. RS(record
    separator)是输入数据的记录的分隔符,简单理解就是可以指定每个记录的结尾标志
  3. RS作用就是表示一个记录的结束
  4. 当我们修改了RS的值,最好配合NR(行)来查看变批,也就是修改了RS的值通过NR查看结果,调试awk程序
  5. ORS输出数据的记录的分隔符

1.4.3 显示Xiaoyu的姓氏和ID号码

[root@zeq files]# awk ' $2~/Xiaoyu/{print $1,$3}' reg.txt  

Zhang 390320151

1.5 字段(列)

FS即field separator,输入字段(列)分隔符。分隔符就是菜刀,把一行字符串切为很多个区域
NF即 number of fileds,表示一行中列(字段)的个数,可以理解为菜刀切过一行后,切成了多少份
OFS输出字段(列)分隔符
awk使用内置变量FS来记录区域分隔符的内容,FS可以在命令行上通过-F参数来更改,也可以通过BEGIN模块来更改
然后通过$n,n是整数,来取被切割后的区域,
3.6 ORS与OFS简介
RS是输入记录分隔符,决定awk如何读取或分割每行(记录)
ORS表示输出记录分隔符,决定awk如何读取或分割每行(记录)
FS是输入区域分隔符,决定awk输出每个区域的时候使用什么分割他们。
awk无比强大,你可以通过RS,FS决定awk如何读取数据。你也可以通过修改ORS,OFS的值指定awk如何输出数据。
{print ,}这里面的逗号等于OFS的值

RS=行分隔符(记录分隔符)
ORS=输出记录分隔符
FS=字段分隔符(列分隔符)
OFS=输出字段分隔符(OFS的值等于逗号)

1.4.4 显示所有以41开头的ID号码的人的全名和ID号码

[root@zeq files]# awk '$3~/^41/{print $1,$2,$3}' reg.txt

Zhang Dandan 41117397

Liu Bingbing 41117483

awk默认动作

[root@zeq files]# awk  '$3~/^41/' reg.txt

Zhang Dandan    41117397   :250:100:175

Liu   Bingbing  41117483   :250:100:175

[root@zeq files]# awk  '$3~/^41/{print }' reg.txt

Zhang Dandan    41117397   :250:100:175

Liu   Bingbing  41117483   :250:100:175

$0=整个记录

3.7 题目
按单词出现频率降序排序

[root@ctg files]# cat count.txt 
root x root root bin bash
bin x bin bin sbin nologin
daemon x daemon sbin sbin nologin
adm x adm var adm sbin nologin
lp x lp var spool lpd sbin nologin
sync x sync sbin bin sync
shutdown x shutdown sbin sbin shutdown
halt x halt sbin sbin halt
mail x mail var spool mail sbin nologin
uucp x uucp var spool uucp sbin nologin

解答:

sed 's# #n#g' count_word.txt|sort |uniq -c 
tr " " "n" < count_word.txt|sort |uniq -c 
awk 'BEGIN{RS=" "}{print $0}' count_word.txt|sort |uniq -c 
egrep -o "[a-z]+" count_word.txt|sort |uniq -c 
xargs -n1 <awk_blank.txt|sort |uniq -c 
awk 'BEGIN{RS=" "}{print $0}' awk_blank.txt|sort |uniq -c 
egrep -o "[a-zA-Z0-9]+" awk_blank.txt|sort |uniq -c

第2章 awk进阶
2.1 awk的模式
 正则表达式作为模式
 比较表达式作为模式
 范围模式
 特殊模式BEGIN和END
awk的模式是你玩好awk的必备也是最基础的内容,必须熟练掌握
2.1 练习题:

mkdir -p /server/files/
cat >>/server/files/reg.txt<<EOF
Zhang Dandan    41117397   :250:100:175
Zhang Xiaoyu    390320151  :155:90:201
Meng  Feixue    80042789   :250:60:50
Wu    Waiwai    70271111   :250:80:75
Liu   Bingbing  41117483   :250:100:175
Wang  Xiaoai    3515064655 :50:95:135
Zi    Gege      1986787350 :250:168:200
Li    Youjiu    918391635  :175:75:300
Lao   Nanhai    918391635  :250:100:175
EOF

显示姓Zhang的人的第二次捐款金额及她的名字

 [root@ctg files]# awk -F "[ :]+" '$1~/Zhang/{print $(NF-1)}' reg.txt 
100
90

显示Xiaoyu的姓氏和ID号码

[root@oldboy34-niubility files]# awk '$2~/Xiaoyu/{print $1,$2,$3}' reg.txt 
Zhang Xiaoyu 390320151

显示所有以41开头的ID号码的人的全名和ID号码

[root@oldboy34-niubility files]# awk '$3~/^41/{print $1,$2,$3}' reg.txt 
Zhang Dandan 41117397
Liu Bingbing 41117483

显示所有以一个D或X开头的人名全名

[root@oldboy34-niubility files]# awk '$2~/^[DX]/{print $1,$2}' reg.txt 
Zhang Dandan
Zhang Xiaoyu
Wang Xiaoai

显示所有ID号码最后一位数字是1或5的人的全名

[root@oldboy34-niubility files]# awk '$3~/[15]$/{print $1,$2}' reg.txt 
Zhang Xiaoyu
Wu Waiwai
Wang Xiaoai
Li Youjiu
Lao Nanhai

显示Xiaoyu的捐款.每个值时都有以$开头.如$520$200$135

[root@ctg files]# awk '/Xiaoyu/{print $NF}' reg.txt |tr ":" "$"
$155$90$201

[root@ctg files]# awk -F "[ :]+" 'BEGIN{OFS="$"}/Xiaoyu/{print  "",$4,$5,$6}' reg.txt 
$155$90$201

awk '{gsub(/:/,"$");print $NF}' reg.txt

显示所有人的全名,以姓,名的格式显示,如Meng,Feixue

[root@ctg files]# awk '{print $1","$2}' reg.txt 
Zhang,Dandan
Zhang,Xiaoyu
Meng,Feixue
Wu,Waiwai
Liu,Bingbing
Wang,Xiaoai
Zi,Gege
Li,Youjiu
Lao,Nanhai

取出下面不是oldboy的行 (匹配结果需包含oldboy linux )

cat >>/server/files/zhuanyedian.txt <<EOF
lidao
oldboy
oldboylinux
zhangdao
xiadao
EOF

[root@oldboy34-niubility files]# awk '$0!~/oldboy$/' zhuanyedian.txt
lidao
oldboylinux
zhangdao
xiadao

1.4.5 显示所有ID号码最后一位数字是1或5的人的全名

方法1

[root@zeq files]# awk '$3~/[15]$/{print $1,$2}' reg.txt     []匹配里面的1和5    $表示以...结尾

Zhang Xiaoyu

Wu Waiwai

Wang Xiaoai

Li Youjiu

Lao Nanhai

方法2

[root@zeq files]# awk '$3~/(1|5)$/{print $1,$2}' reg.txt     |或者   1或者5

Zhang Xiaoyu

Wu Waiwai

Wang Xiaoai

Li Youjiu

Lao Nanhai

2.2 gsub替换函数

[root@ctg test]# cat reg.txt 
Zhang Dandan    41117397     :250:100:175
Zhang Xiaoyu    390330151    :155:90:201
Meng  Feixue    80042789     :250:60:50
Wu    Waiwai    70271111     :250:80:75
Liu   Bingbing  41117483     :250:100:175
Wang  Xiaoai    3515064655   :50:95:135
Zi    Gege      1986787350   :250:168:200
Li    Youjiu    918381635    :175:75:300
Lao   Nanhai    918391635    :250:100:175
[root@ctg test]# awk '{gsub(/:/,"$");print $NF}' reg.txt 
$250$100$175
$155$90$201
$250$60$50
$250$80$75
$250$100$175
$50$95$135
$250$168$200
$175$75$300
$250$100$175

[root@oldboy34-niubility files]# #sed 's###g'
[root@oldboy34-niubility files]# #gsub awk中 进行查找替换 功能 命令 函数
[root@oldboy34-niubility files]# #gsub(/正则表达式/,"替换成什么")  #把一行内 
[root@oldboy34-niubility files]# #gsub(/正则表达式/,"替换成什么",$2) #把$2第二列里面的 内容替换成xxx
[root@oldboy34-niubility files]# 
[root@oldboy34-niubility files]# echo oldboy linux |awk '{gsub(/oldboy/,"oldgirl")}'
[root@oldboy34-niubility files]# echo oldboy linux |awk '{gsub(/oldboy/,"oldgirl");print }'
oldgirl linux
[root@oldboy34-niubility files]# cat 
awkfile.txt      count.txt        oldboy.txt       reg.txt          zhuanyedian.txt  
[root@oldboy34-niubility files]# cat reg.txt 
Zhang Dandan    41117397   :250:100:175
Zhang Xiaoyu    390320151  :155:90:201
Meng  Feixue    80042789   :250:60:50
Wu    Waiwai    70271111   :250:80:75
Liu   Bingbing  41117483   :250:100:175
Wang  Xiaoai    3515064655 :50:95:135
Zi    Gege      1986787350 :250:168:200
Li    Youjiu    918391635  :175:75:300
Lao   Nanhai    918391635  :250:100:175
[root@oldboy34-niubility files]# awk '{gsub(/:/,"$",$NF);print }' reg.txt 
Zhang Dandan 41117397 $250$100$175
Zhang Xiaoyu 390320151 $155$90$201
Meng Feixue 80042789 $250$60$50
Wu Waiwai 70271111 $250$80$75
Liu Bingbing 41117483 $250$100$175
Wang Xiaoai 3515064655 $50$95$135
Zi Gege 1986787350 $250$168$200
Li Youjiu 918391635 $175$75$300
Lao Nanhai 918391635 $250$100$175
[root@oldboy34-niubility files]# awk '{gsub(/:/,"$",$NF);print }' reg.txt |column -t
Zhang  Dandan    41117397    $250$100$175
Zhang  Xiaoyu    390320151   $155$90$201
Meng   Feixue    80042789    $250$60$50
Wu     Waiwai    70271111    $250$80$75
Liu    Bingbing  41117483    $250$100$175
Wang   Xiaoai    3515064655  $50$95$135
Zi     Gege      1986787350  $250$168$200
Li     Youjiu    918391635   $175$75$300
Lao    Nanhai    918391635   $250$100$175

[root@ctg files]# awk '{gsub(/[a-zA-Z]/,"*");print}' reg.txt 
***** ******    41117397   :250:100:175
***** ******    390320151  :155:90:201
****  ******    80042789   :250:60:50
**    ******    70271111   :250:80:75
***   ********  41117483   :250:100:175
****  ******    3515064655 :50:95:135
**    ****      1986787350 :250:168:200
**    ******    918391635  :175:75:300
***   ******    918391635  :250:100:175
[root@ctg files]# awk '{gsub(/[a-zA-Z]/,"*",$1);print}' reg.txt 
***** Dandan 41117397 :250:100:175
***** Xiaoyu 390320151 :155:90:201
**** Feixue 80042789 :250:60:50
** Waiwai 70271111 :250:80:75
*** Bingbing 41117483 :250:100:175
**** Xiaoai 3515064655 :50:95:135
** Gege 1986787350 :250:168:200
** Youjiu 918391635 :175:75:300
*** Nanhai 918391635 :250:100:175

1.4.6 显示Xiaoyu的捐款.每个值时都有以$开头.如$520$200$135

[root@zeq files]# awk -F: -vOFS=$ '/Xiaoyu/{print "$"$2,$3,$4}' reg.txt

$155$90$201          -F指定:为分隔符    -vOFS=$输出$为分隔符   

2.3 比较表达式作为模式

之前我们看了正则表达式在awk下的运用,下面再具体看看比较表达式如何在awk下工作。
awk是一种编程语言,能够进行更为复杂的判断,当条件为真的时候,awk就执行相关的action。主要是在针对某一区域做出相关的判断,比如打印成绩在80分以上的行,这样就必须对这一个区域做比较判断,下表中列出了awk可以使用的关系运算符,可以用来比较数字或者字符串,还有正则表达式。当表达式为真时,表达式结果为1,否则为0,只有表达式为真,awk才执行相关的action
|运算符 含义 示例

< 小于 x>y
<= 小于等于 x<=y
== 等于 x==y
!= 不等于 x!=y
>= 大与或等于 x>=y
> 大于 x<y
以上的运算符均是针对数字,下面两个运算符针对字符串
~ 与正则表达式匹配 x~/y/
!~ 与正则表达式不匹配 x!~y

1.5 awk替换

gsub  awk内置函数

2.4 说明

awk在输出整行既$0的时候,仅仅是原封不动的输出整行,没有任何修改。这就造成一个问题,如果我修改了OFS,那么输出整行的时候print
$0的时候,也不会有任何改变。即:如果awk的action动作没有更改行的内容,OFS(包括ORS)都不会生效。
所以我们需要让awk知道$0被修改了,这一个事情。
$1=$1是把$1的值赋值给$1,这显然不会修改任何内容,但是这个动作会通知awk我修改了$1的内容,所以再次修改print
$0的时候,$0的内容就变化了。
这是一个小技巧

1.5.1 格式  

gsub(/要替换的内容/,”替换成什么”,替换的部分)

2.5 awk特殊模式-BEGIN模式与END模式

1.5.2 接“显示Xiaoyu的捐款.每个值时都有以$开头.如$520$200$135”这一题

[root@zeq files]# awk '{gsub(/:/,"$"); print}' reg.txt       把所有的冒号替换成$

Zhang Dandan    41117397   $250$100$175

Zhang Xiaoyu    390320151  $155$90$201

Meng  Feixue    80042789   $250$60$50

Wu    Waiwai    70271111   $250$80$75

Liu   Bingbing  41117483   $250$100$175

Wang  Xiaoai    3515064655 $50$95$135

Zi    Gege      1986787350 $250$168$200

Li    Youjiu    918391635  $175$75$300

Lao   Nanhai    918391635  $250$100$175

加上条件精确到哪一部分

[root@zeq files]# awk '$2~/Xiaoyu/{gsub(/:/,"$"); print $NF}' reg.txt     $NF最后一列

$155$90$201
2.5.1 BEGIN模块

BEGIN模块在awk读取文件之前就执行,一般用来定义我们的内置变量(预定义变量,eg:FS,
RS),可以输出表头(类似excel表格名称)
BEGIN模式之前我们有在示例中提到,自定义变量,给内容变量赋值等,都使用过。需要注意的是BEGIN模式后面要接跟一个action操作块,包含在大括号内。awk必须在对输入文件进行任何处理前先执行BEGIN里的动作(action)。我们可以不要任何输入文件,就可以对BEGIN模块进行测试,因为awk需要先执行完BEGIN模式,才对输入文件做处理.BEGIN模式常常被用来修改内置变量ORS,RS,FS,OFS等的值.

[root@ctg ~]# awk 'BEGIN{FS=":";print "username","UID","password"}NR==1{print $1,$2,$3}' /etc/passwd|column -t
username  UID  password
root      x    0

1.6 范围

1、从第1行到第5行内容

awk  ‘NR==1,NR==5’

2、从包含某个内容的行到包含某个内容的行

awk  ‘/内容/,/内容/’

2.5.2 awk中变量的概念简介

 直接定义,直接使用即可

awk中字母会被认为是变量,如果真的要给一个变量赋值字母(字符串),请使用双引号。

发表评论

电子邮件地址不会被公开。 必填项已用*标注