目录
一、awk变量
1.1 awk中变量的分类
awk中表达式可以使用的变量有以下三类
- 用户定义的:变量名可以由字母、下划线和数字组成。
- 内建变量:变量名都以大写字母组成。
- 字段变量:变量名的形式为$number。
而在awk程序中,变量的类型是不需要声明的,awk程序会自动根据上下文判断变量的类型。未初始化的变量的值默认为空或0。
除了字段变量外,引用内建变量和用户自定义变量都不需要符号“”$。
1.2 内建变量
变量 | 意义 | 是否会被重置 | 默认值 |
ARGC | 命令行参数的个数 | 否 | 无 |
ARGV | 命令行参数数组 | ||
FILENAME | 当前输入的文件名 | 每当有一个新的文件被读取,该值就会被重置 | |
NF | 当前记录的字段数 | 每当awk从输入流中读取新的一行时就会被重置 | |
FS | 输入流的字段分割符 | " " 空格 | |
NR | 当前已读取的记录数 | 无 | |
FNR | 文件总的的记录数 | 否 | |
OFMT | 数值的输出格式 | ".6g" | |
OFS | 输出字段分割符 | 无 | |
ORS | 输出记录分割符 | "\n" | |
RS | 输入流的记录分割符 | ||
RLENGTH | 被函数match匹配的字符串长度 | 无 | |
RSTART | 被函数match匹配的字符串的开始 | 随着match的调用而被重置 | |
SUBSEP | 下标分割符 | "\034" |
简单的几点说明:
内建变量可以理解为是awk程序为了方便的进行统计和字段、记录切割操作而默认提供的变量。不需要定义即可直接使用。一般都在BEGIN设置。
内建变量和普通变量一样,可以在PROGRAM中的任何地方修改,使用起来更加灵活。
1.2.1 ARGV与ARGC
ARGV是一个数组,内部元素的值是命令行参数的值。可以通过ARGV[INDEX]的形式调用各命令行参数。其中:
ARGV[0]是awk程序名
往后各元素的值则是命令行参数的值。
最后一个元素是awk程序作用的文件名。
ARGC是ARGV count,统计了ARGV的元素个数。
[tyson@Tyson Lee testawk]$ awk 'BEGIN{print ARGC,ARGV[0],ARGV[1],ARGV[2],ARGV[3],ARGV[4]}' a="var1" b="var2" c="var3" text.txt
5 awk a=var1 b=var2 c=var3 text.txt
ARGV是可以自行修改的
[tyson@Tyson Lee testawk]$ awk 'BEGIN{ARGV[5]="newVar";print ("newARGC="ARGC".we can use ARGV[5],ARGV[5]="ARGV[5])}' a="var1" b="var2" c="var3" text.txt
newARGC=5.we can use ARGV[5],ARGV[5]=newVar
1.3 字段变量
什么是字段?-对每一条记录用字段分割符分割开的是一个个字段。
什么是字段变量?-对字段进行引用的变量就是字段变量(和shell的位置参数类似)。例如:
- $0:对当前记录进行整体引用
- $1:对当前记录的第一个字段进行引用
- $2:对当前记录的第二个字段进行引用
- $NF:对当前记录的最后一个字段进行引用
- 可以进行算数运算,$(NF-1):对当前记录的倒数第二个字段进行引用
- 每一行的字段数量可以不同,通常上线为一百。
字段变量和普通变量一样,可以被用在patter或action的表达式中,可以被重新赋值。当然被若添加或删除了字段变量则相应的变量如$0、ARGV、ARGC、NF等都会发生改变。
[tyson@Tyson Lee testawk]$ awk 'BEGIN{FS=":";OFS="\t"};$4>=500{$4="system_user"};$4<500{$4="normal_user"};{$8="hi";print $1,$4,$8}' /etc/passwd|grep tyson
tyson system_user hi
tyson1 system_user hi
1.4 所能对变量进行的运算
算数运算、赋值、三目运算、逻辑运算、匹配运算、关系运算、拼接运算、单目运算、自增自减运算等。
其中比较重要的有:
逻辑运算(只有0或1这两种值,运算符两边的称为操作数)
exp1 $$ exp2
exp1 || exp2
三目运算符(条件表达式)
exp1?exp2:exp3
其他不再赘述
二、awk流程控制
在awk的PROGRAM中的action部分中,可以使用和C语言类似的语法,因此同样会涉及到流程控制的问题。主要的问题是在命令行中和在自行指定的awk文件中的写法有区别(其实就是单行书写和多行书写的不同规范问题)。但是begin不能再流程控制语句中,因为按照定义,BEGIN这个模式只能够被识别一次。
2.1 语句组
#多行模式
statement1
statement2
statement3
#单行模式
statement1;statement2;statement3
2.2 if-else
#多行模式
{
if (expression)
statements
else
statements2
}
#单行模式
{if (expression) statements1;else statements2}
2.3 while
while (expression) {statement1;statement2;statement3}
2.4 for
for {expression1;expression2;expression3}
等价于 expression1;while(expression2){statments;expression3]
#三个表达式都是可省的,exp2省略是永真循环
2.5 do-while
do statments while (expression)
#先执行do,用while判断,然后循环执行do
2.6 break、continue
中断距离break或continue最近的for、while或do-while的这一次循环,选择退出循环或执行下一轮循环。
2.7 next、exit
控制的是用于读取输入行动作的程序循环。
next:抓取下一输入行并按顺序执行PROGRAM部分的(判断是否匹配+执行action)。
exit:整个awk程序不会再读取下一行并直接开始执行模式END的ACTION块,若exit在模式END的ACTION块中出现则直接退出程序。(默认退出状态码为0,可以通过exit expression指定退出状态码)。
三、awk数组
awk内数组与数组元素都不需要事先声明,也不需要说明数组中有多少个元素(length)。具有直接使用、自动扩展的特性。
被提及时则数组元素被创建,元素默认初始值是空。
awk中的数组是关联数组。
awk中数组的下表可以是字符串或数值。
- 字符串下标是多功能的数组下标,arr[1]与arr["1"]是同一个元素(因为“1”的字符串值是1),但是通常不这么使用。
- 字符串下标“01”与字符串下标“1”对应的值并不相同
数组具体说明
8 | "foo" | "" | 30 | Value |
0 | 1 | 2 | 3 | Index |
元素对应的value会占内存空间,下标是不占空间的。
3.1 awk数组是关联数组
何为关联数组?
- 每个数组都是对(索引及其对应的数组元素值)的集合
- 索引不一定是字数,可以是数字或字符串。
- 索引可以是乱序的,不一定递增。
- 关联数组中,索引可以是整数也可以是字符串。
3.2 实例1:倒序输出
[tyson@Tyson Lee testawk]$ cat text.txt
SR 8649 275 Asia
Canada 3852 25 North_America
China 3705 1032 Asia
USA 3615 237 North_America
Brazil 3286 134 South_America
India 1267 746 Asia
Mexico 762 78 North_America
France 211 55 Europe
Japan 144 120 Asia
Germany 96 61 Europe
England 94 56 Europe
[tyson@Tyson Lee testawk]$ awk '{x[NR]=$0};END{for(i=NR;i>0;i--){print x[i]}}' text.txt
England 94 56 Europe
Germany 96 61 Europe
Japan 144 120 Asia
France 211 55 Europe
Mexico 762 78 North_America
India 1267 746 Asia
Brazil 3286 134 South_America
USA 3615 237 North_America
China 3705 1032 Asia
Canada 3852 25 North_America
SR 8649 275 Asia
NR表示目前已读取的行数,{x[NR]=$0} 表示对每一行都执行这个对数组赋值的操作,也就是按顺序将每一行录入到数组。
当PROGRAM执行到END时,NR已经是整个文件的行数了,所以END中的内容则是倒叙输出。
3.3 实例2:遍历数组
遍历数组的方式是:
for (name in array){statements}
name每次赋予的值是index的值。
[tyson@Tyson Lee testawk]$ cat text.txt
SR 8649 275 Asia
Canada 3852 25 North_America
China 3705 1032 Asia
USA 3615 237 North_America
Brazil 3286 134 South_America
India 1267 746 Asia
Mexico 762 78 North_America
France 211 55 Europe
Japan 144 120 Asia
Germany 96 61 Europe
England 94 56 Europe
[tyson@Tyson Lee testawk]$ awk '{pop[$4]+=$2};END{for (name in pop) {print name,pop[name]}}' text.txt
North_America 8229
Asia 13765
Europe 401
South_America 3286
在这里需要注意的是,若在数组中使用了未定义的变量做下表,例如我未定义Test的值,那么array[Test]的值相当于array[""]。
3.4 实例3:操作数组
数组的格式array_name[index]
数组的赋值array_name[1]=val或array_name["string"]=val
数组的引用array_name[1]或array_name["string"]
需要注意的点:
当数组某元素的下标(index)不存在于该数组时,会创建有该下标对应的元素,value为空。这种操作经常导致内存崩溃。
3.5 如何判断是否存在某个index
使用index in array表达式用于测试特定的索引所以是否存在,若不存在也不会创建该元素,避免了内存崩溃。
3.6 获取数组里面是否有某个value
只能遍历并对比数组的所有元素
3.7 删除数组元素
delete array[index]
for (i in array)
delete array[i]
3.8 字符串切割和存储
split函数split(str,arr,fs)
str:输入字符串
arr:用来存放切割好的字段的数组
fs:字段分割符
返回值:切割完成后字段的个数。
[tyson@Tyson Lee testawk]$ awk '{split($0,temp_array," ");print temp_array[1];for (i in array){delete temp_array[i]}}' text.txt
SR
Canada
China
USA
Brazil
India
Mexico
France
Japan
Germany
England
[tyson@Tyson Lee testawk]$ cat text.txt
SR 8649 275 Asia
Canada 3852 25 North_America
China 3705 1032 Asia
USA 3615 237 North_America
Brazil 3286 134 South_America
India 1267 746 Asia
Mexico 762 78 North_America
France 211 55 Europe
Japan 144 120 Asia
Germany 96 61 Europe
England 94 56 Europe