Awk命令

一、基础概念

awk命令是Linux文本处理三剑客之一,其man文档把功能概况为pattern scanning and processing language(模式扫描机处理语言),通俗来讲awk命令是一个文本报告生成器,用于实现格式化文本输出,在处理文本文件时对文档中的某字段按条件执行操作,作者为aho、weinberger、kernighan,命令名称由其名字的首字母组成。awk命令大概出现在1970年左右的unix系统中,后来在开源领域不得不把awk所有的功能在Linux中重新实现,现在Linux用到的awk其实是gawk命令

二、命令用法

命令格式:gawk [option] 'program' File…

1. option

  • -F:指明输入字段时的分隔符
  • -v var=value:自定义变量,变量名区分大小写
    内建变量:
    	FS:input field separator,输入时的字段分隔符,默认为空白字符
    	OFS:out field separator,输出时字段的分隔符,默认为空白
    	RS:input record separator,输入时的换行符
    	ORS:output record separator,输出时的换行符
    	NF:number of field,每一行的字段数量
    	NR:number of record,文件中的行数
    	FNR:file number of record,对多个文件分别计数
    	FILENAME:显示文件名
    	ARGC:命令行中参数个数
    	ARGV[0]:命令行中所给定的各参数,需要指定下标[#]
    

2. program

programpatter(模式)及用一对花括号包括的ACTION STATEMENTS(动作语句)组成

(1)PATTER

在awk里是类似地址定界,可在patter前加!表示取反之意

  • empty:不定义patter,处理文本文件的每一行

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

  • END{}:仅在文本处理完成之后,命令之前执行一次

~]# awk -F: 'BEGIN{print "username  uid"}{printf "%-10s %-4d\n",$1,$3}END{print "=============="}' /etc/passwd
username  uid
root       0   
bin        1   
daemon     2   
adm        3   
lp         4   
sync       5   
shutdown   6   
halt       7   
mail       8   
==============
[root@node1 source]# 
  • /regular expression/:写明正则表达式,仅处理匹配行
    显示fstab中以uuid开头的行:awk '/^UUID/{print}' /etc/fstab
  • relational expression:关系表达式,结果为真的才被处理,非0为真,非空串为真
    显示id号大于100的用户:awk -F: '$3>100{print $1,$3}' /etc/passwd
  • /pat/,/pat/:指明两个关键字,匹配之间的内容

(2)ACTION STATEMENTS

动作语句,语句中用分号分割

① print命令

print item,item2...:item之间用逗号隔开,item可以是字符串、数值、当前记录的字段、变量或awk表达式,省略item,相当于打印$0

② printf命令

printf "FORMAT",item1,item2:完成格式化输出的命令,通过FORMAT设定格式,然后把每一个item对位放在FORMAT中的每个格式符的位置。FORMAT必须要给出,其输出信息不会自动换行,需要在FORMAT中显示给出换行控制符\n才能完成换行

格式符:
	%c:显示字符的ASCII码
	%d、%i:显示十进制整数
	%e、%E:科学技术法数值显示
	%f:显示为浮点数
	%g、%G:以科学计数法或浮点形式数值显示
	%s:显示字符串
	%u:无符号整数
	%%:显示%自身
	
修饰符:%#[.#]
	%3.1f:第一个数字控制显示宽度,第二个#表示小数点后精度
	%-:左对齐
	%+:显示数值的符号
  • 示例一:显示/etc/passwd文件中的用户名称
[root@192 sh]# awk -F: '{printf "Username: %s\n",$1}' /etc/passwd
Username: root
Username: bin
Username: daemon
Username: adm
Username: lp
.......

给定printf命令,用一组双引号将FORMAT包含,FORMAT中使用格式符%s为后面的变量进行占位并控制格式,处理时把$1的值替换到%s处,最后在手动指定\n换行,FORMAT和item之间使用逗号分割

  • 示例二:显示/etc/passwd文件中的用户名称和ID号
[root@192 sh]# awk -F: '{printf "Username: %s  UID: %d\n",$1,$3}' /etc/passwd
Username: root  UID: 0
Username: bin  UID: 1
Username: daemon  UID: 2
Username: adm  UID: 3
Username: lp  UID: 4
.......

同示例一,只不过示例二给定两个格式符%s和%d,awk会自动将两个item对位替换至格式符处

  • 示例三:显示/etc/passwd文件中的用户名称和ID号,加入修饰符
[root@192 sh]# awk -F: '{printf "Username: %10s  UID: %4d\n",$1,$3}' /etc/passwd
Username:       root  UID:    0
Username:        bin  UID:    1
Username:     daemon  UID:    2
Username:        adm  UID:    3
Username:         lp  UID:    4
Username:       sync  UID:    5

示例三使用格式符+修饰符%10s和%4d,对输出信息进行宽度控制,只不过对其方式为右对齐

  • 示例四:显示/etc/passwd文件中的用户名称和ID号,加入修饰符并调整为左对齐
[root@192 sh]# awk -F: '{printf "Username: %-10s  UID: %-4d\n",$1,$3}' /etc/passwd
Username: root        UID: 0   
Username: bin         UID: 1   
Username: daemon      UID: 2   
Username: adm         UID: 3   
Username: lp          UID: 4   
Username: sync        UID: 5   
③ expressions:条件表达式

格式为:selector?if-true:if-false

操作符:
    算数运算操作符:  x+y、x-y、x*y、x/y、x%y、-x、+y
    赋值操作符: =、+=、-=、/=、%=、^=、++、--
    数值比较操作符:>、>=、<、<=、!=、==
    模式匹配符:~、!~
    逻辑操作符:&&、||、!
    定义的变量要加双引号
  • 示例一:判断用户的id是否大于1000,如果大于则显示user,否则显示sys
    awk -F: '{$3>=1000?type="user":type="sys";printf "username: %-10s UID: %-5d type: %-7s\n",$1,$3,type}' /etc/passwd
username: root                  UID: 0      type: sys      
username: nobody                UID: 65534  type: user   
username: sssd                  UID: 992    type: sys    
username: zhangsan              UID: 1000   type: user 

通过对第三个字段的数值进行判断,如果大于1000则复制type变量为user,小于则赋值type变量为sys,变量赋值时一定要加上双引号

④ control(控制语句)
if语句用法
  • 单分支:if(condition) statements,单分支语句不用加花括号
    condition:条件
    statements:条件为真的语句
示例一:显示uid大于10的用户

]# awk -F: '{if($3>10) print $1,$3}' /etc/passwd
operator 11
games 12
ftp 14
nobody 65534
dbus 81
.......
示例二:如果用户的默认shell为bash,则显示其用户名及其shell

~]# awk -F: '{if($NF=="/bin/bash") print $1,$NF}' /etc/passwd
root /bin/bash
zhangsan /bin/bash
lisi /bin/bash
示例三:如果某行字段大于5段,则显示之
~]# awk '{if(NF>5) printf "%-95s   %-2d\n",$0,NF}' /etc/fstab
# Created by anaconda on Tue Apr 20 03:57:24 2021                                                 10
# Accessible filesystems, by reference, are maintained under '/dev/disk/'.                        9 
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info.                      12
# After editing this file, run 'systemctl daemon-reload' to update systemd                        11
# units generated from this file.                                                                 6 
UUID=c077882c-9433-4d90-ba75-79c137a956b7 /                       xfs     defaults        0 0     6 
  • 双分支:if(condition) {statements} else {statements}
    else后跟条件为假的语句,注意加话括号
示例二:显示uid大于1000则输出user,小于则输出sys
[root@node1 source]# awk -F: '{if($3>1000) {printf "%-10s %-4s\n",$1,"user"} else {printf "%-10s %-4s\n",$1,"sys"}}' /etc/passwd
root       sys 
bin        sys 
daemon     sys 
adm        sys 
lisi       user
while循环

while循环格式:var;while(condition) {statements}
第一次判断时如果为假则一次都不循环,一般使用在对一行内的多个字段逐一进行处理时使用,也可对数组中的元素处理时。循环前先定义变量i,用分号分割i的修正语句

示例一:遍历文件的每个字段,对每个字段做长度统计
~]# awk '/[[:space:]]*search/{i=1;while(i<=NF) {print $i,length($i); i++}}' /etc/grub2.cfg
if 2
[ 1
x$feature_platform_search_hint 30
= 1
xy 2
]; 2
then 4
search 6
--no-floppy 11
--fs-uuid 9
--set=root 10
--hint-bios=hd0,msdos1 22
--hint-efi=hd0,msdos1 21
--hint-baremetal=ahci0,msdos1 29
--hint='hd0,msdos1' 19
7b9dd426-d07f-49cb-b500-5f04fa478bb2 36

首先使用patter搜索包含search字符串的行,接着定义变量i,当i的数值小于本行的长度NF的数值时,打印当前i的内容及用内建函数length()统计的数值,最后做变量i数值修正

do while循环

格式:do {statements} while(condition)
不管条件为真为假,都会先执行一遍循环体

for循环

for:可以遍历数组元素。
格式:for(变量赋值;条件判断;变量修正) statements

 示例一:遍历文件的每个字段,对每个字段做长度统计
~]# awk '/[[:space:]]*search/{for(i=1;i<=NF;i++) print $i,length($i)}' /etc/grub2.cfg
if 2
[ 1
x$feature_platform_search_hint 30
= 1
xy 2
]; 2
then 4
search 6
--no-floppy 11
--fs-uuid 9
--set=root 10
--hint-bios=hd0,msdos1 22
--hint-efi=hd0,msdos1 21
--hint-baremetal=ahci0,msdos1 29
--hint='hd0,msdos1' 19
7b9dd426-d07f-49cb-b500-5f04fa478bb2 36
示例二:同示例一题目,只不过显示长度大于20的字段
~]# awk '/[[:space:]]*search/{for(i=1;i<=NF;i++) {if(length($i)>=20) print $i,length($i)}}' /etc/grub2.cfg
x$feature_platform_search_hint 30
--hint-bios=hd0,msdos1 22
--hint-efi=hd0,msdos1 21
--hint-baremetal=ahci0,msdos1 29
7b9dd426-d07f-49cb-b500-5f04fa478bb2 36
7b9dd426-d07f-49cb-b500-5f04fa478bb2 36
x$feature_platform_search_hint 30
--hint-bios=hd0,msdos1 22
--hint-efi=hd0,msdos1 21
--hint-baremetal=ahci0,msdos1 29
7b9dd426-d07f-49cb-b500-5f04fa478bb2 36
7b9dd426-d07f-49cb-b500-5f04fa478bb2 36

示例二中嵌套了一个if语句,需要注意嵌套的if语句要包含在花括号内

  • for循环的特殊用法:for(var in array) {statements}:此种方式会把数组array的下标赋值给变量var,接着就可以遍历数组中的元素了
switch语句

语法格式:switch (expression) {case value1 or /regexp/: statement; case value2 or /regexp/;default:statement}

循环控制控制符
  • break[#]:退出n层循环
  • continue:提前结束这轮循环,进入下一轮
  • next:awk中的特殊语句,提前结束awk对本行的处理,而进入下一行
  • exit:退出

三、awk中的数组

关联数组格式:array[index]

1. index的类型

可以有以下几种常见的种类

  • 普通索引:可使用任意字符串,字符串要加双引号
  • 为声明索引:如果某索引元素实现不存在,在引用时awk会自动创建此元素,并将其初始为空,如果做数值计算,这个元素就会被当成0使用。需要注意如果想知道某个数组是否存在某元素,要使用index in array 格式进行

2. 定义数组

定义格式:array["index_expression"]=""

  • ~]# awk 'BEGIN{day["1"]="monday";day["2"]="tuesday";print day["1"]}'
    monday
  • ~]# awk 'BEGIN{day["1"]="monday";day["2"]="tuesday";print day["2"]}'
    tuesday

3. 遍历数组

遍历数组中的元素需要使用for循环时,变量i会替换成数组的下标

[root@node1 ~]# awk 'BEGIN{day["1"]="monday";day["2"]="tuesday";for(i in day) {print day[i]}}'
monday
tuesday
示例一:使用awk统计链接的状态
[root@centos6 ~]# netstat -tan
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address               Foreign Address             State      
tcp        0      0 0.0.0.0:22                  0.0.0.0:*                   LISTEN      
tcp        0     64 192.168.1.250:22            192.168.1.208:13455         ESTABLISHED 
tcp        0      0 192.168.1.250:22            192.168.1.208:12766         ESTABLISHED 
tcp        0      0 :::22                       :::*                        LISTEN      
[root@centos6 ~]# netstat -tan | awk '/^tcp/{state[$NF]++} END{for(i in state) {print i,state[i]}}'
ESTABLISHED 2
LISTEN 2
示例二:遍历nginx日志,查看访问次数大于10的ip地址
[root@node1 nginx]# awk '{ip[$1]++}END{for(i in ip) {if(ip[i]>10) {printf "%-20s  count: %-4s\n",i,ip[i]}}}' bak | sort -nk3
40.77.167.105         count: 11  
144.91.106.14         count: 14  
34.211.221.72         count: 15  
168.119.249.94        count: 16  
34.221.89.26          count: 17  
45.95.53.153          count: 17  
52.225.230.5          count: 18  
8.214.35.220          count: 18  
103.97.201.132        count: 21  
111.196.124.162       count: 23  
119.8.177.149         count: 31  
61.49.79.64           count: 45  
117.74.135.35         count: 980 

四、awk中的函数

做数值处理

  • rand():返回0和1之间的随机小数

字符串处理

  • length([s]):返回指定字符串的长度
  • sub(r,s,[t]):以r所表示的模式,查找t所表示的字符串,并将其第一次出现替换为s所表示的内容
  • gsub(r,s,[t]):以r所表示的模式,查找t所表示的字符串,并将其所有替换为s所表示的内容
  • split(s,a[,r]):以r为分割符切割字符s,并将其切割后的结果保存至a所表示的数组中,数组编号从1开始

五、练习

  1. 统计/etc/fstab文件中,每个文件系统的个数
    awk '!/^#|^$/{filetype[$3]++}END{for(i in filetype)print i,filetype[i]}' /etc/fstab
    awk '/^UUID/{type[$3]++}END{for(i in type) print i,type[i]}' /tmp/fstab
  2. 统计指定文件中,每个单词出现的个数;
    awk '{for(i=1;i<=NF;i++) word[$i]++}END{for(i in word) print i,word[i]}' /etc/fstab
  3. 如果用户的id号大于1000,就输出系统用户,小于则显示普通用户
    awk -F: '{if($3>1000){printf "Username: %-15s Common User\n",$1} else {printf "Username: %-15s Admin user\n",$1}}' /etc/passwd
  4. 如果用户shell为/bin/bash,则显示用户名与bash
    awk -F: '$NF=="/bin/bash"{printf "Username: %-15s %10s\n",$1,$7}' /etc/passwd
    awk -F: '{if($NF~"/bin/bash") {printf "Username: %-15s %10s\n",$1,$NF}}' /etc/passwd
  5. 如果某一行字段大于5个,就显示整个字段,否则不显示
    awk -F: '{if(NF>5) {print}}' /etc/passwd
  6. df -h显示的设备空间空间大于10则显示
    df -h | awk -F% '{print $1}' | awk 'BEGIN{print "===========devuse==========="}/^\//{if($NF>10) {printf "devname: %-5s use: %d%%\n",$1,$NF }}'
  7. 打印每个字段以及字段的长度
    awk '/^[[:space:]]*linux16/{i=1;while(i<=NF) {print $i,length($i);i++}}' /etc/grub2.cfg
    awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub2.cfg
  8. 打印每个字段以及字段的长度,仅显示大于等于7的
    awk '/^[[:space:]]*linux16/{i=1;while(i<=NF) {if(length($i)>=7) {print $i,length($i)}i++}}' /etc/grub2.cfg
    awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {if(length($i)>=7) {print $i,length($i)}}}' /etc/grub2.cfg
  9. 显示用户id号为偶数的用户
    awk -F: '{if($3%2!=0)next;print $1,$3}' /etc/passwd
  10. ss查看状态,取出各状态名以及出现次数
    ss -tan | awk '!/^State/{sstype[$1]++}END{for(i in sstype){print i,sstype[i]}}'
  11. 查看/var/log/httpd/access_log,每个ip对服务器请求
    awk '{ip[$1]++}END{for(i in ip){print i,ip[i]}}' /var/log/httpd/access_log
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值