awk 实用案例介绍
awk 简介
• awk是 3 个姓氏的首字母,代表该语言的 3 个作者
• awk的版本有很多,包括: 旧版 awk,新版 awk(nawk), GNUawk(gawk)等
• awk程序有 awk命令、括在引号或写在文件中的指令以及输入文件这几个部分组成
从文件输入
• 格式:
–gawk '/匹配字符串/' 文件名
–gawk '{处理动作}' 文件名
–gawk '/匹配字符串/ {处理动作}' 文件名
实例:
[root@desktop243 Desktop]# gawk -F: '/student/{print $3}' /etc/passwd 500 #-F:以":"号为分隔符分割出参数 [root@desktop243 Desktop]# gawk -F: '/student/{print $1,$3}' /etc/passwd student 500 [root@desktop243 Desktop]# gawk -F: '/student/{print $0}' /etc/passwd student:x:500:500::/home/student:/bin/bash
awk 工作原理
以下面的内容的 names 文件名举例按步骤解析 awk 的处理过程
[root@desktop243 Desktop]# vim names Tome Savage 100 Molly Lee 200 John Doe 300
编辑这个 names文件时,Tome 行是多个出来空格,Molly 行是一个出来空格,John 是空格键打出来的空格。使用下面 awk命令处理
cut 无法识别空格键和<Tab>打出来的空格 [root@desktop243 Desktop]# cat names | cut -d ' ' -f 1 Tome Molly Lee John [root@desktop243 Desktop]# cat names | cut -d ' ' -f 2 Savage Doe gawk能区分空格键和<Tab>打出来的空格,“,”是代表打印时$1 和$3 之间有空格 [root@desktop243 Desktop]# gawk '{ print $1,$3}' names Tome 100 Molly 200 John 300
第一步:awk对文件或管道的内容一次只处理一行,将获取到
的这一行赋给内部变量 $0
![](https://img2018.cnblogs.com/blog/1444147/201905/1444147-20190528164649619-93582279.png)
第二步:这一行的内容按 awk内部变量 FS 定义的分隔符,缺省为空格(包括 tab 制表符)分解成字段,每一段存储在从 $1 开始的变量中
![](https://img2018.cnblogs.com/blog/1444147/201905/1444147-20190528164716545-748838459.png)
第三步:awk中 print 命令打印字段
–{print $1,$3} #只取有用的第一段和第三段
–在打印时$1 和$3 之间由空格间隔。
","逗号是一个映射到内部的输出字段分隔符(OFS),OFS 变量缺省为空格,逗号在输出时被空
格替换
Tome 100
Molly 200
John 300
• 接下来,awk处理下一行数据,直到所有的行处理完
实例:
OFS,输出字段分隔符定义为两个制表符,“,”在输出时被制表符替换 [root@desktop243 Desktop]# gawk '{ OFS="\t\t";print $1,$3}' names Tome 100 Molly 200 John 300
从命令输入
• awk还可以处理通过管道接收到的 Linux 命令的结果,shell程序通常使用 awk 做深处理
• 格式:
–命令 | gawk '/匹配字符串/'
–命令 | gawk '{处理动作}'
–命令 | gawk '/匹配字符串/ {处理动作}'
df | gawk '$4 > 200000' #剩余空间大于 200000 的磁盘
[root@desktop243 Desktop]# df | gawk '$3 > 100' Filesystem 1K-blocks Used AvailableUse% Mounted on 16126920 2940932 12366788 20% / tmpfs 477200 260 476940 1% /dev/shm /dev/sda1 99150 58800 35230 63% /boot 516040 21940 467888 5% /home
格式化输出 print 函数
awk命令操作处理部分是放在 "{}"(括号)中
print 函数将变量和字符夹杂着输出,如同 linux 中的 echo 命令
[root@desktop243 Desktop]# date Sat Oct 15 16:15:18 CST 2011 [root@desktop243 Desktop]# date | gawk '{print "Month: "$2"\nYear:",$6}' Month: Oct Year: 2011
注意上面的例子,一种是直接在Month空格后连接$2,另一种是在Year和$6 之间使用了逗号,都由 OFS 决定
OFMT 变量
在 OFMT 中定义数字的格式
模拟分区大小的转换,保留小数点后 2 位: [root@desktop243Desktop]#echo -e "/dev/sda1\t1234325\n/dev/sda3\t2209"\ > | gawk '{OFMT="%.2f";print $1":",$2/1024,"M"}' /dev/sda1: 1205.40 M /dev/sda3: 2.16 M
#$2/1024 前后必须要有","作为分隔定义,如果没有其中一个就会不能实现打印小数点后 2
位
默认为"%.6gd",只会打印 6 位
[root@desktop243Desktop]#echo -e "/dev/sda1\t1234325\n/dev/sda3\t2209"\
|gawk '{prrnt $1":",$2/1024,"M"}'
/dev/sda1: 1205.4 M
/dev/sda3: 2.15723 M
printf 函数转义字符
printf 与 C 语言中的 printf 雷同
–转义字符
[root@desktop243 Desktop]# cat names Tome Savage 100 Molly Lee 200 John Doe 300 names 文件中第 2 列的第一个字符:-%c 字符 [root@desktop243 Desktop]# gawk '{printf("The charcter is %c\n",$2)}' names The charcter is S The charcter is L The charcter is D names 文件中第 2 列的字符串:–%s 字符串 [root@desktop243 Desktop]# gawk '{printf("The string is %s\n",$2)}' names The string is Savage The string is Lee The string is Doe 打印一个人的名字和年龄:–%d 十进制整数 规定好了是字符串或者整数,后面的变量就必须是与之匹配的类型,否则会出错。 [root@desktop243 Desktop]# echo "yangwawa 10"|gawk \ > '{printf("%s is %d years old.\n",$1,$2)}' yangwawa is 10 years old.
–%f 浮点数
[root@desktop243 Desktop]# echo "yangwawa 10 155"|gawk\ > '{printf("%s is %d years old,his heigth si %.2fm.\n",$1,$2,$3/100)}' yangwawa is 10 years old,his heigth si 1.55m.
printf 函数修饰符
打印时需要对齐,下面提供一些打印输出时所用到的修饰符
-(横杠) 左对齐和右对齐(默认),加| |是为了突出长度和对齐效果:
[root@desktop243 Desktop]# echo "Bluefox" | gawk '{printf("Welcome to \ > |%s| lab\n",$1)}' Welcome to |Bluefox|lab [root@desktop243 Desktop]# echo "Bluefox" | gawk '{printf("Welcome to\ > |%10s|lab\n",$1)}' Welcome to | Bluefox|lab [root@desktop243 Desktop]# echo "Bluefox" | gawk '{printf("Welcome to > |%-10s|lab\n",$1)}' Welcome to |Bluefox |lab
#(井号) 显示 8 进制时前面加 0,显示 16 进制时加 0x
+(加号) 显示正负值时的正+负-号
0(零) 用 0 对显示值填充空白处
练习:将文件(之前很乱的)names整理并保存,要求每一行每一列都要对齐:
[root@desktop243 Desktop]# gawk '{printf("%s\t\t%s\t\t%d\n",$1,$2,$3)}' \ >names > name ;rm -f names ;mv name names [root@desktop243 Desktop]# cat names Tome Savage 100 Molly Lee 200 John Doe 300
文件中的 awk 命令
• 将 awk写入一个文件中,更适合复杂的程序
• 使用 -f 选项指定 awk的文件名
• awk一次读取一条记录,测试文件中的每一条命令这样循环
根据下面显示结果分析代码:
执行结果: [root@desktop243 Desktop]# cat names | gawk -f abc.awk ________--------________ Nice to meet you -->Molly ________--------________ ________--------________ John 代码: [root@desktop243 Desktop]# cat abc.awk #!/bin/gawk /^[Mm]olly/{print "Nice to meet you -->"$1} {print "________--------________"} $3>200 {print $1} 被测试的文件: [root@desktop243 Desktop]# cat names Tome Savage 100 Molly Lee 200 John Doe 300 分析: 程序开始后首先读到的$0 是 Tome Savage 100,开始的字符既不是 M 也不是m, 后 面 的 {print "Nice to meet you -->"$1}不 会 执 行 , 执 行 {print "________ --------________"}后,判断 100 不大于 200,则 {print $1}不执行;接着$0 是 Molly Lee 200,首字符串匹配,执行{print "Nice to meet you -->"$1}的结果是 Nice to meet you -->Molly,然后执行下一句,执行最后一句时发现 200 不大于 200, {print $1}不执行;最后传给$0的是John Doe 300,第一句由于不匹配而没有 执行,执行完第二句代码,最后 300 大于 200,所以执行{print $1},结果是显示 John。
小小地修改一下代码:
#!/bin/gawk /^[Mm]olly/{print "Nice to meet you -->"$1;\ print "________--------________"} $3>200 {print $1}
再测试,结果: [root@desktop243 Desktop]# gawk -f abc.awk names Nice to meet you -->Molly ________--------________ John
记录与字段
记录分隔符:默认行输入和输出的分隔符都是回车 ,保存在 RS 和 ORS 内部变量中
实例:
默认: [root@desktop243 Desktop]# gawk '{print $1,$2}' names Tome Savage Molly Lee John Doe 自定义: [root@desktop243 Desktop]# gawk 'ORS="\n+-+\n"{print $1,$2}' names Tome Savage +-+ Molly Lee +-+ John Doe +-+
变量$0: awk每次一行取得整条记录,$0 随之改变,同时内部变量 NF(字段的总数,即列数)也随之变化
实例:添加一个只有 2 列的数据,测试 NF:
[root@desktop243 Desktop]# echo "Mayy Daly" >> names [root@desktop243 Desktop]# gawk '{print NF,$0}' names 3 Tome Savage 100 3 Molly Lee 200 3 John Doe 300 2 Mayy Daly
变量 NR: 每条记录的行号,处理完一行将会加 1,所以全部处理完后可以理解成行数的总数
[root@desktop243 Desktop]# gawk '{print NR,": ->",$0}' /etc/passwd 1 : -> root:x:0:0:root:/root:/bin/bash 2 : -> bin:x:1:1:bin:/bin:/sbin/nologin …… …… 38 : -> student:x:500:500::/home/student:/bin/bash 39 : -> visitor:x:501:501::/home/visitor:/bin/bash
字段分隔符
• FS 内部变量:
– 保存着输入字段的分隔符的值 (OFS 则代表输出的分隔符)
– 默认使用空格或制表符来分隔字段
– 在 BEGIN 语句段中设置 FS 的值
实例: [root@desktop243Desktop]# cat /etc/passwd | gawk 'BEGIN{FS=":";print"--count normal user now--"}$3 >= 500 { print $1;count++} END {printf("Total : %d Normal users\n",count)}' --count normal user now-- nfsnobody student visitor Total : 3 Normal users
也可以在命令行中指定 -F 选项改变分隔符
默认是以空格为分隔符 [root@desktop243 Desktop]# gawk 'NR <= 4 {print NR,$1}' /etc/passwd 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 指定分隔符为“:” [root@desktop243 Desktop]# gawk -F : 'NR <= 4 {print NR,$1}' /etc/passwd 1 root 2 bin 3 daemon 4 adm
• 使用多个字符分隔符,写在括号中,下面的例子使用空格,冒号和制表符
– gawk -F'[ :\t]' '{print $1, $5, $7 }' /etc/passwd
模式
awk模式用来控制输入的文本行执行什么样的操作
• 模式为正则表达式
• 模式具有着隐式 if 语句
• 模式写在模式操作符两个 "//"中
实例: [root@desktop2 Desktop]# gawk '/^root/' /etc/passwd root:x:0:0:root:/root:/bin/bash [root@desktop2 Desktop]# gawk '/^root/ || /^student/' /etc/passwd root:x:0:0:root:/root:/bin/bash student:x:500:500::/home/student:/bin/bash [root@desktop2 Desktop]# gawk -F: '/student/{print $1":"$3}' /etc/passwd student:500 [root@desktop2 Desktop]# gawk '/^sshd/,/^stude*/' /etc/passwd sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin nslcd:x:65:55:LDAP Client User:/:/sbin/nologin tcpdump:x:72:72::/:/sbin/nologin pulse:x:496:494:PulseAudio System Daemon:/var/run/pulse:/sbin/nologin gdm:x:42:42::/var/lib/gdm:/sbin/nologin student:x:500:500::/home/student:/bin/bash
操作
• 格式
模式 { 操作语句 1; 操作语句 2; ...... ;}
• 或者
模式 { 操作语句 1 操作语句 2 ...... }
模式 { 操作语句 1 操作语句 2 ...... }
正则表达式
• 很多地方都是用到正则表达式来匹配特定的信息
^ 串首
$ 串尾
. 匹配单个任意字符
* 匹配零个或多个前面的字
+ 匹配一个或多个前面的字符
? 匹配零个或一个前面的字符
[ABC] 匹配括号中给出的任一个字符
[A-Z] 匹配 A到 Z之间的任一个字符
A|B 匹配二选一,或者的意思,等同于[AB]
(AB)+ 匹配一个或多个括号中的组合
\* 星号本身,转义星号本身
匹配操作符
• 前面介绍了模式,也可以对特定的列使用模式匹配符"~",与条件做比较
• 语法:
• ~//
或者
!~ / /
[root@desktop2 Desktop]# gawk -F: '$1 ~ /[sS]tud*/{print $1":"$3}' /etc/passwd student:500
POSIX 字符类表达式
[[:allnum:]] 字母和数字字符 [A-Za-z0-9]
• [[:alpha:]] 字母字符等同于[A-Za-z]
• [[:cntrl:]] 控制字符
• [[:digit:]] 数字字符 [0-9]
• [[:graph:]] 非空白字符(空格,控制字符等)
• [[:lower:]] [[:upper:]] 小写/大写字母
• [[:space:]] 所有的空白字符(换行符,空格,制表符)
一般不会用这些,这些不好记。
gawk 'BEGIN { FS=":"} $1 ~ /^[[:digit:]]+$/ {print $0 }' /etc/inittab
awk 脚本
• 如果有多条 awk的模式或指令要处理,将它们都写在脚本中
• #为注释符
• 一行多条命令要用分号隔开
• 操作跟在模式之后的话,必须一行书写完(即左大括号在同一行)
[root@desktop2 Desktop]#vim info #!/bin/gawk # The first awk script /student/ { print "Studentid is" , $2} /vistor/ {print "vistor id is", $2} 运行: [root@desktop2 Desktop]#gawk -F: -f info /etc/passwd
比较表达式
用来对文本做比较,只有条件为真,才执行指定的动作
• < gawk '$3 < 500' /etc/passwd #系统帐户
• <= gawk '$3 <= 499' /etc/passwd #同上
• == gawk '$3 == 0' /etc/passwd #id 为 0,root 帐户
• != gawk '$3 != 500' /etc/passwd #非 id=500 的帐户
• >= gawk '$3 >= 500' /etc/passwd #普通帐户
• > gawk '$3 > 499' /etc/passwd #同上
• ~ gawk '$1 ~ /^root/' /etc/passwd #root 帐户
• !~ gawk '$1 !~ /^root/' /etc/passwd #非 root 帐户
条件表达式
• 格式:条件表达式 1?表达式 2:表达式 3
• 与之等同的代码段如下
{
if (条件表达式 1 成立)
表达式 2
else
表达式 3
}
实例:
[root@desktop2Desktop]#echo "111 333"| gawk '{max=$1>$2?$1:$2;print max}' 333 [root@desktop Desktop]# cat /etc/passwd | gawk -F: '{printf("%s is %s user\n",$1,$3 >= 500?"Normal":"System") }' rootis System user bin is System user …… …… student is Normal user visitor is Normal user
算术运算
可以在模式中执行计算操作
awk '$3 / 1024 > 2000' filename
+ 加 10+2 =12
- 减 10-2 =8
* 乘 10*1024 =10240
/ 除 10240/1024 =10
% 求模(求余数) 10%3=1
^ 次方 2^3=8
逻辑操作符
逻辑操作符用来测试模式的真假
&& 逻辑与 1&&0 FALSE
|| 逻辑或 1 || 0 TRUE
! 逻辑非 !0 TRUE
awk -F: '$3 >500 && $3 <= 550' /etc/passwd
awk -F: '$3 ==100 || $3 > 50'
范围模式
范围模式提供了选择一段数据操作的可能
• 先匹配第一个模式,将作为开始部分
• 接着匹配第二个模式,作为结束
• 之间的这一段将选中做操作
• 模式与模式之间使用","逗号间隔
awk '/operator/,/nobody/' /etc/passwd
验证数据的有效性
验证数据的有效性可以综合我们之前所学的判断方式
• 判断有效数据是否为七列,可以通过检测 NF 内部变量
实例:找出 employees数据表中的缺空数据
[root@shiyan ~]# cat employees Name Numbers Time Price Tome 24 2008/10/12 3000 Marry 38 2008/11/8 5433 Molly 70 2009/2/17 3421 John 55 2009/4/9 5646 Tome 65 2009/8/7 Molly 45 2009/9/21 John 77 2009/10/8 5687 [root@shiyan ~]# gawk 'NF !=4 {print NR,": has only",NF,"fileds"}' employees 6 : has only 3 fileds 7 : has only 3 fileds
• 判断是否为字母开头
awk '$1 ~ /^[a-zA-z]+/ {.....}' filename
• 判断数值是否大于某值
awk '$4 > 200 {......}' filename
数值变量和字符串变量
字符串写下双引号中,比如"Hello world"
• 默认将一个字符串转化成数字时,将变成 0
–name = "Nancy" #name 此时为字符串
–x++ #x 是数字,初始值为 1
–number = 35 #number 是数字
• 强行转化
–name + 0 #name 此时变成数字 0
–number " " #number 此时为字符
用户自定义变量
变量名可以是字母数字和下划线组成,但不能以数字开头。
• awk将字符串变量初始化为空字符串 ""
• 数值变量初始化为 0
• 格式
–变量 = 表达式
[root@sya ~]#gawk'$1~/^T/{wage=$2*$4;print$1,$3,"wage:",wage}' ./employees Tome 2008/10/12 wage: 72000 Tome 2009/8/7 wage: 0
BEGIN 模式
BEGIN 模式后面跟了一个程序段
• 对输入文件进行任何处理之前,先执行 BEGIN 程序段
• 用来修改内部变量(OFS,RS,FS)等的 值
• 用来初始化用户变量和打印标题等
[root@shiyan ~]# gawk 'BEGIN{FS=":";OFS="\t\t";ORS="\n****\n"} NR >=20 && NR <= 23{print $1,$NF}' /etc/passwd smmsp /sbin/nologin **** nscd /sbin/nologin **** oprofile /sbin/nologin **** pcap /sbin/nologin **** [root@desktop1 Desktop]# echo "$(date +%F)" | gawk 'BEGIN{FS="-";} \ > { print $1"/"$2"/"$3}' 2011/10/18
甚至无需文件名
[root@shiyan ~]# gawk 'BEGIN{print "Hello World"}' Hello World
END 模式
不匹配任何输入行
• 在 awk处理完输入行之后执行
[root@shiyan ~]# gawk 'END{print "Total user:",NR}' /etc/passwd Total user: 36
实例:查看虚拟主机有几台:
[root@shiyan ~]#gawk '/^<Virt/ { total++ } END { print total, virtual hosts }' /etc/httpd/httpd.conf 2 virtual hosts
输出重定向
可以将 awk 的输出重定向到 Linux 文件中
• 目标文件必须用双引号括起来
• "> " 清空文件,写入 awk信息,awk程序执行完毕后才关闭文件
• ">>" 追加内容
实例:将 employees 文件中的错误数据向屏幕输出,正确数据则将每个人每天的总金额输出到/root/cc 文件中:
[root@shiyan ~]# vim rw.awk #!/bin/gawk { \ if ( NF !=4 ) \ { \ print "Line",NR,":missing some info" \ }else{ \ print $1,"saled:",$2*$4 > "/root/cc"\ } \ } 测试: [root@shiyan ~]# gawk -f rw.awk employees Line 5 :missing some info Line 6 :missing some info [root@shiyan ~]# cat cc Tome saled: 72000 Marry saled: 206454 Molly saled: 239470 John saled: 310530 John saled: 437899
输入重定向-读输入 getline
从标准输入,管道或文件中读取输入
• 读取每一行,重置 NF,NR 和 FNR 内部变量
• 有内容读取到,返回 1 (为真值)
• 读到 EOF(文件末尾) 返回 0 (为假值)
• 发生错误,返回 -1
[root@shiyan ~]# gawk 'BEGIN{"date"| getline DATE}{print DATE,$1,$7}' /etc/passwd 2011 年 10 月 15 日 星期六 09:41:49 CST root:x:0:0:root:/root:/bin/bash 2011 年 10 月 15 日 星期六 09:41:49 CST bin:x:1:1:bin:/bin:/sbin/nologin …… …… [root@shiyan ~]# gawk 'BEGIN{"date +%F"| getline DATE}{print DATE,$1,$7}' /etc/passwd 2011-10-15 root:x:0:0:root:/root:/bin/bash 2011-10-15 bin:x:1:1:bin:/bin:/sbin/nologin …… ……
实例:提示用户输入一个用户名,判断该用户是在/etc/passwd 中的哪一行:
[root@shiyan ~]# gawk 'BEGIN{print"What is your name?";\ >getline name < "/dev/tty"}\ >$1 ~ name{print "Found",name,"online",NR".";\ >print"NIce to meet you",name,"@_@"}' /etc/passwd Whatis your name? student #getline 是重定向到终端接受的 Found student online 34. NIce to meet you student @_@
Bash 向 awk 发送参数
getline 函数也可以从 Bash 命令发送的管道中获取参数
• 语法:
–getline 变量 < "-" # "-" 如同 tar 一样代表着管道过来的信息
[root@shiyan ~]# echo "$(date +%F)"|gawk 'BEGIN{ FS=":";\ >getline D < "-"}NR>=33{print D" "$1"/"$3":"$7}' /etc/passwd 2011-10-15 avahi-autoipd/100:/sbin/nologin 2011-10-15 student/500:/bin/bash 2011-10-15 visitor/501:/bin/bash 2011-10-15 oracle/502:/bin/bash
管道
打开管道的前提是其他管道必须关闭,每次只能打开一个管道,这和 Linux 中一条命令无
限次的使用管道不同
• 管道右边的命令必须写在双引号之间
[root@shiyan ~]# gawk '/^[^#:]+/{print $1}' /etc/hosts 127.0.0.1 172.24.205.191 172.24.200.254 172.24.254.254 [root@shiyan ~]# gawk '/^[^#:]+/{print $1 |"ping -c2 "$1}' /etc/hosts PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data. 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.028 ms connect: Network is unreachable connect: Network is unreachable connect: Network is unreachable 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.037 ms --- 127.0.0.1 ping statistics --- 2 packets transmitted, 2 received, 0% packetloss, time 999ms rtt min/avg/max/mdev = 0.028/0.032/0.037/0.007 ms
关闭文件和管道
要对 awk 程序中的某个文件或管道多次读写,得先关闭程序
• awk程序在处理文件时会保持打开状态直到脚本结束。
• END 块中也受管道影响。
• 关闭文件或管道时的引号中的内容与打开文件或管道时的名称
一致,甚至包括参数,空格
• 使用 close()函数实现
gawk '/^[^#]+/ { print $1 | "ping -c3 " $1 } END {close("ping -c 3 ")}' /etc/hosts
system 函数
• system()函数以 Linux 命令作为参数
• 执行 Linux 命令之后将 Linux 命令 $? 退出值返回给 awk
• 注意:函数中 Linux 命令必须用双引号
[root@desktop1 Desktop]# gawk '/^[^#:]/{ if(system("ping -c2 " $1" > /dev/null")==0 ){print $1" is on line"}else{ print $1" is down"}}' /etc/hosts 127.0.0.1 is on line 172.24.0.2 is down 172.24.200.254 is on line 172.24.254.254 is on line 注意里面的空格“ ” !
if 语句
• awk就如 C 语言,有着变量,条件判断和循环
• if 语句是条件判断的关键字,默认为隐藏,
• 格式
if (判断的表达式)
{
语句 1;语句 2;…… #判断表达式(为真)成立而执行的代码段
}
实例:
1、gawk -F: '{if( $3 >= 500) print $1 "is a normal user\n"}'/etc/passwd
2、 gawk -F: '{ if ($3 >=500) {count++;} } END { print "Total normal user: "count }' /etc/passwd
if/else 语句
• if/else 语句实现了真与假的两重处理
• 判断表达式为 1(真),与前面 if 语句相同
• 为 0 (假)则执行 else 后面的代码段
• 格式:
if (判断表达式) {
语句 1;语句 2;…… #判断表达式成立
} else {
语句 10,语句 11;…… #判断表达式不成立
}
实例 1:
gawk -F: '{ if($3 >=500) { print $1 "is a normal user\n" } else { print $1 "is
a system user\n"}' /etc/passwd
实例 2:
gawk -F: '{ if($3 < 500) { print $1 "is a system user\n" } else { print $1 "is a
normal user\n"}' /etc/passwd
if/else 和 else if 语句
• 如果在计算多重判断的时候,我们还需要对 if/else 语句做扩充,在其后再加上 if else,做
下一个判断
语法
if (判断表达式 1) {
语句 1; 语句 2;…… #表达式 1 成立
}else if (判断表达式 2) {
语法 10;语法 11;...... #表达式 2 成立
}else if (判断表达式 N...) {
语法 N0;语法 N1;…… #表达式 N 成立
}else {
语法 Y1;语法 Y2;.... #以上都不成立
}
while 循环
首先给初始一个变量,接着在 while 判断表达式中测试该变量,为 0(假)退出 while 循环;
• 注意:代码段中要在一些情况下修改初始的变量值,否则是一个消耗 CPU 的死循环
• 语法
while(条件判断式){
语句 1;语句 2;…… # 语句成立一直执行部分
}
[root@desktop Desktop]# gawk -F: '{print NR,":User info:\n======";i=1; while(i<=NF){print $i;i++};print "\n"}' /etc/passwd 1 :User info: ====== root x 0 0 root /root /bin/bash …… …… 39 :User info: ====== visitor x 501 501 /home/visitor /bin/bash
for 循环
有着三个表达式,第一个为初始化变量,第二个做测试,第三个用来改变初始变量(如果缺少
此部分,就得到代码段修改,否则是死循环)
• 语法
for(初始表达式; 判读表达式; 更新表达式) {
语法 1; 语法 2; .....
}
[root@desktop Desktop]# gawk -F: '{print NR,":User info:\n======"; i=1;for(;i<=NF;i++){print $i};print "\n"}' /etc/passwd 1 :User info: ====== root x 0 0 root /root /bin/bash 2 :User info: ====== bin x 1 1 bin /bin /sbin/nologin …… ……
循环控制
break 用来终止循环
• continue 语句 用来不做后续操作,绕过此次循环,继续下一循环
实例:
为了方便实验,创建一个/tmp/passwd: [root@localhost ~]# cat /tmp/passwd avahi-autoipd:x:100:103:avahi-autoipd:/var/lib/avahi-autoipd:/sbin/nologin student:x:500:500::/home/student:/bin/bash visitor:x:501:501::/home/visitor:/bin/bash harry:x:502:502::/home/harry:/bin/bash [root@desktop Desktop]# gawk -F: '{i=1; while(i++ < NF ){ if($i~/daemon/) {print NR,$1;break;} } }' /etc/passwd 3 daemon 28 haldaemon 31 avahi continue 语句:会将匹配的这行其他段打印出来,继续往下查询 [root@localhost ~]# cat /tmp/passwd | gawk -F: '{ i=0; while(i++ < NF){if($i~/student/){ print "========";continue; } else { print $i ;}}}' avahi-autoipd x 100 103 avahi-autoipd /var/lib/avahi-autoipd /sbin/nologin ======== x 500 500 ======== /bin/bash visitor x 501 501 /home/visitor /bin/bash harry x 502 502 /home/harry /bin/bash break 语句:匹配的这行其他段都不会打印,继续往下查询 [root@localhost ~]# cat /tmp/passwd | gawk -F: '{ i=0; while(i++ < NF){if($i~/student/){ print "========";break; } else { print $i ;}}}' avahi-autoipd x 100 103 avahi-autoipd /var/lib/avahi-autoipd /sbin/nologin ======== visitor x 501 501 /home/visitor /bin/bash harry x 502 502 /home/harry /bin/bash
next 语句
在循环处理时,使用 next 语句,将会使 awk 中跳过 next 段
以后本次的处理,执行下一个循环
• 下面的例子将打印出除系统帐户以外的所有用户
[root@desktop18 Desktop]# gawk -F: '{if($3<500){next;}else{print $1}}' /etc/passwd nfsnobody student visitor
exit 语句
exit 用来终止 awk 对后续内容的处理
• exit 也可以返回具体的值,提供 Bash 做状态判断
• 要注意,exit 退出不会绕过 END 块,换句话说 END块
总会执行,对于要向 BASH 返回值的处理,使用以下的方法
• 语法:
–{ exit(1) }
实例:
exit,找到匹配的就会退出,返回 1,不再往下查询: [root@localhost ~]# cat /tmp/passwd | gawk -F: '{ i=0;while(i++ < NF){if($i~/student/){ print "========";exit(1); } else { print $i ;}}}' avahi-autoipd x 100 103 avahi-autoipd /var/lib/avahi-autoipd /sbin/nologin ======== [root@localhost ~]# echo $? 1
关联数组的下标
• awk中的数组下标可以是数字,也可以是字符串
• 数组和变量一样,需要的时候直接创建
• 数组的下标数值由 0 开始
[root@desktop18 Desktop]# vim employess Tom 234 2008/03/05 685 Mary 451 2008/04/06 932 Bill 127 2009/10/09 3456 Mary 341 2009/10/18 4532 Tom 465 2009/11/10 3421 [root@desktop18 Desktop]# gawk '{ name[k++]=$1} END{for(i=0;i<NR;i++){ printi,name[i]}}' employess 0 Tom 1 Mary 2 Bill 3 Mary 4 Tom
用字符串作为数组的下标
• 专门用于数组的 for 循环,遍历数组
• for(索引 in 数组)
{
–语句
}
![](https://img2018.cnblogs.com/blog/1444147/201905/1444147-20190528171049095-1603214272.png)
打印出用户名和总工资:数组下标:字符串
将用户名($1)相同的行的工资($4)相加 [root@desktop18 Desktop]# gawk '{ name[$1]+=$4} END{for(Name in name){ print Name,":",name[Name]}}' employess Tom : 4106 Mary : 5464 Bill : 3456
处理命令行参数
从命令行获得参数,ARGC代表参数的总数,ARGV数组用来保存输入的参数
[root@desktop18 Desktop]# vim argvs #!/bin/gawk BEGIN{\ for ( i=0;i < ARGC ; i++ )\ { \ printf( "ARGV[%d] is --> %s\n",i,ARGV[i])\ }\ printf( "Total : %d parameters\n",ARGC);\ } [root@desktop18 Desktop]# gawk -f argvs /etc /root 1234 172.24.200.254 ARGV[0] is --> gawk ARGV[1] is --> /etc ARGV[2] is --> /root ARGV[3] is --> 1234 ARGV[4] is --> 172.24.200.254 Total : 5 parameters
awk不会把 -f 以及后面的脚本认定为参数
[root@desktop18 Desktop]# gawk -f argvs /etc "ls /root" ARGV[0] is --> gawk ARGV[1] is --> /etc ARGV[2] is --> ls /root Total : 3 parameters
用 xargs来接收保存命令传来的参数并传递:(以空隔来分别参数)
[root@desktop18 Desktop]# echo "172.24.200.254" | xargs ping -c2 PING 172.24.200.254 (172.24.200.254) 56(84) bytes of data. From 192.168.117.167 icmp_seq=1 Destination Host Unreachable From 192.168.117.167 icmp_seq=2 Destination Host Unreachable --- 172.24.200.254 ping statistics --- 2 packets transmitted, 0 received, +2 errors, 100% packetloss, time 3000ms pipe 2 [root@desktop18 Desktop]# echo "172.24.200.254 /etc/passwd /root" | xargs gawk -f argvs ARGV[0] is --> gawk ARGV[1] is --> 172.24.200.254 ARGV[2] is --> /etc/passwd ARGV[3] is --> /root Total : 4 parameters
字符串 sub 和 gsub 函数
sub 和 gsub 函数,可以在条目中查找与给定的正则表达式匹配的字符串,并取代它
• sub 和 gsub 的区别是,前者只对匹配部分一次替换,后者为全部替换
• 语法:
–sub( 正则表达式 , 替换字符串) #默认为$0,整条记录
–sub( 正则表达式,替换字符串 , 目标字段); #指定的字段
换名字:
[root@desk18Desktop]# gawk '{sub(/[Tt]om/,"ttooomm",$1);print $0}' employess ttooomm 234 2008/03/05 685 Mary 451 2008/04/06 932 Bill 127 2009/10/09 3456 Mary 341 2009/10/18 4532 ttooomm 465 2009/11/10 3421 sub:只对匹配部分一次替换 [root@desktop18 Desktop]# gawk '{sub(/0/,"kkkkk");print $0}' employess Tom 234 2kkkkk08/03/05 685 Mary 451 2kkkkk08/04/06 932 Bill 127 2kkkkk09/10/09 3456 Mary 341 2kkkkk09/10/18 4532 Tom 465 2kkkkk09/11/10 3421 gsub 全部替换 [root@desktop18 Desktop]# gawk '{gsub(/0/,"kkkkk");print $0}' employess Tom 234 2kkkkkkkkkk8/kkkkk3/kkkkk5 685 Mary 451 2kkkkkkkkkk8/kkkkk4/kkkkk6 932 Bill 127 2kkkkkkkkkk9/1kkkkk/kkkkk9 3456 Mary 341 2kkkkkkkkkk9/1kkkkk/18 4532 Tom 465 2kkkkkkkkkk9/11/1kkkkk 3421
sub 函数示例
• 注意正则表达式的使用方法
• gawk '{ sub(/172\.168\.0\./, "192.168.0." ); print}'/etc/sysconfig/iptables
• gawk '{ sub(/Mac/,"MacIntosh",$1) ; print }' filename
• gawk '{ gsub(/[Tt]om/, "Thomas" , $1); print }' datafile
字符串长度 length 函数
• length 函数取回字符串的字符数量
• 格式
–length(字符串)
–length #不带参数,返回记录中的字符个数
带参数: [root@desktop18 Desktop]# echo "123qwe" | gawk 'BEGIN{getline RR <"-";print length(RR)}' 6 [root@desktop18 Desktop]# gawk '{print $1,length($1)}' employess Tom 3 Mary 4 Bill 4 Mary 4 Tom 3 不带参数: [root@desktop18 Desktop]# gawk '{print $1,length}' employess Tom 22 Mary 23 Bill 24 Mary 24 Tom 24
字符 substr 函数
• substr函数返回从字符串指定位置开始的一个子字符串。
• 如果指定了子字符串的长度,返回字符串的对应的部分
• 语法:
–substr(字符串,起始位置)
–substr(字符串,起始位置,子字符串长度)
[root@desktop18 Desktop]# gawk '{print substr($1,2,length)}' employess om ary
ill
ary
om
[root@desktop18 Desktop]# gawk '{print substr($3,1,4)}' employess
2008
2008
2009
2009
2009
将 employess 中的日期换成另一种格式
[root@desktop18 Desktop]# gawk '{print $1,$2,
substr($3,9,2)"/"substr($3,6,2)"/"substr($3,1,4),$4}' employess
Tom 234 05/03/2008 685
Mary 451 06/04/2008 932
Bill 127 09/10/2009 3456
Mary 341 18/10/2009 4532
Tom 465 10/11/2009 3421
字符 match 函数
• match 函数根据正则表达式返回其在字符串中出现的位置,未出现,返回 0
• match 函数中变量
RSTART 子字符串出现的起始位置
RLENGTH 子字符串的长度
而这些变量之后可以提供给 substr 来提取子字符串
实例:文件中的时间格式不同的情况下需要提取年份
[root@desktop18 Desktop]# gawk '{print $3}' employess 2008-03-05 2008-04-06 09/11/2009 2009-10-18 2009-11-10 [root@desktop18 Desktop]# gawk '{match($3,/[12][0-9][0-9][0-9]/); print $1,substr($3,RSTART,RLENGTH)}' employess Tom 2008 Mary 2008 Bill 2009 Mary 2009 Tom 2009
字符 split 函数
• split 函数用来将一个字符串拆分成一个数组
• 语法:
–split(字符串, 保存数据的数组,字段分隔符)
#注意 Split 函数切割的数组下标从 1 开始,0 永远为空
–split(字符串, 保存数据的数组 ) #使用 FS 默认值
[root@desktop18 Desktop]# cat database 2010-04-22 car 10 1000 2010-05-10 car 7 700 2010-05-13 dog 8 80 2010-06-11 bike 1 100 统计 5 月份的销售总额: [root@desktop18 Desktop]# gawk '{ >split($1,DATE,"-"); > if(DATE[2] == 5){ > count+=$4 > } >} >END{ > print "May : "count >}' database May : 780
整数 int 函数
• int 函数去掉小数点后面的数字,仅仅留下整数部分
• 它不会做四舍五入操作
[root@desktop18 Desktop]# gawk 'END {print 31/3}' database
10.3333
[root@desktop18 Desktop]# gawk 'END {printint(31/3)}' database
10
生成随机数
• rand 函数生成一个大于或等于 0,小于 1 的浮点数
• 当多次执行上面的脚本,每次都生成相同的数字
• srand 函数,以当前时刻为 rand()生成一个种子
• srand(x)把 x 设置为种子,通常,程序应该在运行过程中不断的改变 x 的值
当没有种子时,生成的随机数不同文件相同行的随机数是一样的,文件有多少行就生成多少个随机数
[root@desktop18 Desktop]# gawk '{print NR,rand()}' database 1 0.237788 2 0.291066 3 0.845814 4 0.152208 [root@desktop18 Desktop]# gawk '{print NR,rand()}' /etc/passwd 1 0.237788 2 0.291066 3 0.845814 4 0.152208 …… …… 38 0.377663 39 0.899756 当有种子时,生成的随机数不同文件或同一文件相同行的随机数是不一样的,文件有多少行 就生成多少个随机数 [root@desktop18 Desktop]# gawk 'BEGIN{srand()}{print NR,rand()}' database 1 0.78965 2 0.970641 3 0.284081 4 0.819256 [root@desktop18 Desktop]# gawk 'BEGIN{srand()}{print NR,rand()}' database 1 0.532605 2 0.737259 3 0.105589 4 0.0879184
技能掌握测试题:
•1 分析下面数据中,打印出每个销售员 5 月销售的总额
vi sales
Tom 2010-04-09 car 6 6000
Mary 2010-05-07 car 1 1000
Tom 2010-05-20 bike 15 1500
Mary 2010-05-22 car 2 2000
Tom 2010-06-17 car 1 1000
•2 以下的数据需要转成 SQL 语句,方便插入到数据库中,注意年月日的格式
vi sales
Tom 04/09/2010 car 6 6000
Mary 05/07/2010 car 1 1000
Tom 05/20/2010 bike 15 1500
Mary 05/22/2010 car 2 2000
SQL 格式
insert into sales value('Tom',2010-04-09,'car',6,6000)
题1: [root@desktop215 Desktop]# gawk '{ > split($2,DATE,"-"); 先将日期的月份拆分成一个数组 > if(DATE[2] == 5){ > name[$1]+=$5} > } # 如果月份是 5 月,就将以姓名(字符串)为数组下标,将相同名字的销售额相 > END{ > for(Name in name){ > print Name,": 05",name[Name]} > }' sales Tom : 05 1500 Mary : 05 3000 方法 2: [root@desktop215 Desktop]# gawk ' $2 ~ /05/{name[$1]+=$5} END{ for(Name in name){ print Name,": 05",name[Name]} }' sales Tom : 05 1500 Mary : 05 3000
题二:
[root@desktop215 Desktop]# gawk '{OFS="\t"; print $1, substr($2,7,4)"-"substr($2,4,2)"-"substr($2,1,2), $3,$4,$5}' sales2 > /root/Desktop/sales3 [root@desktop215 Desktop]# cat /root/Desktop/sales3 Tom 2010-09-04 car 6 6000 Mary 2010-07-05 car 1 1000 Tom 2010-20-05 bike 15 1500 Mary 2010-22-05 car 2 2000 导入数据库: mysql> use bluefox; Database changed mysql> create table sale( name varchar(10), time date, spes varchar(10), num int(11), money int(11) ); Query OK, 0 rows affected (0.11 sec) mysql> select * from sale; +------+------------+------+------+-------+ | name | time | spes | num | money | +------+------------+------+------+-------+ | Tom | 2010-04-09 | car | 6 | 6000 | | Mary | 2010-05-07 | car | 1 | 1000 | | Tom | 2010-05-20 | bike | 15 | 1500 | | Mary | 2010-05-22 | car | 2 | 2000 | +------+------------+------+------+-------+ 4 rows in set (0.00 sec)