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
第二步:这一行的内容按 awk内部变量 FS 定义的分隔符,缺省为空格(包括 tab 制表符)分解成字段,每一段存储在从 $1 开始的变量中
第三步:awk中 print 命令打印字段
–{print $1,$3} #只取有用的第一段和第三段
–在打印时$1 和$3 之间由空格间隔。
","逗号是一个映射到内部的输出字段分隔符(OFS),OFS 变量缺省为空格,逗号在输出时被空
格替换
Tome 100
Molly 200
John 300
• 接下来,awk处理下一行数据,直到所有的行处理完
实例:
OFS,输出字段分隔符定义为两个制表符,“,”在输出时被制表符替换
[root@desktop243 Desktop]# gawk '{ OFS="		";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"
Year:",$6}'
Month: Oct
Year: 2011
注意上面的例子,一种是直接在Month空格后连接$2,另一种是在Year和$6 之间使用了逗号,都由 OFS 决定

OFMT 变量

在 OFMT 中定义数字的格式
模拟分区大小的转换,保留小数点后 2 位:
[root@desktop243Desktop]#echo -e "/dev/sda1	1234325
/dev/sda3	2209"
> | 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 1234325 /dev/sda3 2209"
|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
",$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
",$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.
",$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.
",$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
",$1)}'
Welcome to |Bluefox|lab
[root@desktop243 Desktop]# echo "Bluefox" | gawk '{printf("Welcome to
> |%10s|lab
",$1)}'
Welcome to | Bluefox|lab
[root@desktop243 Desktop]# echo "Bluefox" | gawk '{printf("Welcome to
> |%-10s|lab
",$1)}'
Welcome to |Bluefox |lab
#(井号) 显示 8 进制时前面加 0,显示 16 进制时加 0x
+(加号) 显示正负值时的正+负-号
0(零) 用 0 对显示值填充空白处
练习:将文件(之前很乱的)names整理并保存,要求每一行每一列都要对齐:
[root@desktop243 Desktop]# gawk '{printf("%s		%s		%d
",$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="
+-+
"{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
",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'[ : ]' '{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 ...... }

正则表达式

• 很多地方都是用到正则表达式来匹配特定的信息
^ 串首
$ 串尾
. 匹配单个任意字符
* 匹配零个或多个前面的字
+ 匹配一个或多个前面的字符
? 匹配零个或一个前面的字符
[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
",$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="		";ORS="
****
"} 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
20111015 日 星期六 09:41:49 CST root:x:0:0:root:/root:/bin/bash
20111015 日 星期六 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 "}'/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 " } else { print $1 "is
a system user "}' /etc/passwd
实例 2:
gawk -F: '{ if($3 < 500) { print $1 "is a system user " } else { print $1 "is a
normal user "}' /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:
======";i=1;
while(i<=NF){print $i;i++};print "
"}' /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:
======";
i=1;for(;i<=NF;i++){print $i};print "
"}' /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 数组)
{
–语句
}
打印出用户名和总工资:数组下标:字符串
将用户名($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
",i,ARGV[i])
}
printf( "Total : %d parameters
",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="	"; 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)
原文地址:https://www.cnblogs.com/zoujiaojiao/p/10938735.html