gawk 模式处理语言
gawk 工具是一种模式扫描和处理语言,它搜索一个或者多个文件,以查看这些文件中是否存在匹配指定模式的记录。每次发现匹配时,它通过执行动作的方式来处理文本行。
gawk 属于数据驱动语言:描述想要处理的数据并告诉gawk 当它发现这些数据时将要做的事情。
语法:
gawk [options] [program] [file-list]
gawk [options] -f program-file [file-list]
program: 用户在命令行中所包含的gawk 程序,为了阻止shell 将gawk 命令解释为shell 命令,需要使用单引号将其引起来。
program-file: 存放gawk 程序的文件的名称,将较长或较复杂的程序放置在文件中以减少错误和重复输入。
file-list:gawk 要处理的普通文件的路径名,这些文件就是输入文件;如果没有指定,gawk 将从标准输入获取输入或getline 或协进程指定输入。
语言基础
gawk 程序( 来自命令行或者程序文件) 由一行或者多行文本构成,其中包含一个模式和动作,格式如下:
pattern {action}
pattern: 模式,用来从输入中选取文本行;如果程序行中不包含模式,gawk 将选取输入中的所有行。
action: 对于由模式选中的每行文本,gawk 工具都执行动作action ;如果程序行中没有包含动作,gawk 将把选中的行复制到标准输出。
如果多个模式选中同一行文本,gawk 将按照这些模式出现在程序中的顺序分别执行与每个模式关联的动作。
模式
用斜杠把正则表达式包装起来,就可以将其作为模式使用。~ 操作符用于测试某个字段或者变量是否匹配正则表达式。!~ 用于测试不匹配。可以使用布尔操作符||(OR) 或者&&(AND) 来组合任何模式。
BEGIN 和END 两个独特的模式,分别是在gawk 开始处理之前和处理之后要执行的命令。
, 逗号是范围查找符。如果在一个gawk 程序行上用逗号将两个模式隔开,gawk 选取从匹配第1 个模式的第1 行开始的文本行。gawk 选取的最后一行是匹配第2 个模式的那行紧接着的下一行文本。查找第2 个模式后,将再次查找第1 个模式以再次开始这个过程。
动作
如果gawk 匹配某个模式,它将执行gawk 命令的动作部分所指定的动作。如果没有指定动作,gawk 将执行默认动作,即print 命令( 可用{print} 显示表示) 。除非用逗号将print 命令中的各项区分开,否则gawk 将它们连接起来。逗号使得gawk 用输出字段分隔符(OFS ,通常是空格符) 将各项分隔开来。
通过用分号将多个动作隔开,可以在同一行上包含多个动作。
注释
gawk 工具不处理以# 号开头的程序行中的任何内容。
变量
尽管不需要在使用gawk 变量之前声明变量,但用户可以选择向这些变量赋初值。没有赋值的数值变量初始化为0 ,而字符串变量则被初始化为空串。除了用户变量之外,gawk 还维护了程序变量。在模式部分和动作部分均可使用用户变量和程序变量。
程序变量:
变 量 |
含 义 |
$0 |
当前记录( 作为单个变量) |
$1-$n |
当前记录中的字段 |
FILENAME |
当前输入文件名(null 表示标准输入) |
FS |
输入字段分隔符 |
NF |
当前记录的字段数目 |
NR |
当前记录的记录编号 |
OFS |
输出字段分隔符 |
ORS |
输出记录分隔符 |
RS |
输入记录分隔符 |
除了可以在程序中初始化变量以外,还可以在命令行上使用-v 选项初始化变量。如果某个变量值在gawk 的两次运行之间发生改变,这个特性就非常有用。
默认情况下,输入记录和输出记录的分隔符均为换行符;输入字段分隔符是空格和TAB ,输出字段分隔符是空格。任何时刻均可以修改分隔符的值。
选项
-v
-F
函数
length(str),int(num),index(str1,str2),split(str,arr,del),sprintf(fmt,args),substr(str,pos,len),sub(str,pos,len),tolower(str),toupper(str).
算数操作符
来自C 编程语言。
关联数组
数组使用字符串作为索引。在使用关联数组时,用户可以用数值字符串作为索引来模仿传统数组。
对关联数组元素赋值
array[string] = value
array: 数组名称
string: 数组元素在数组中的索引
value: 要指派的值
printf
类似于C 语言中的printf :
printf “control-string”,arg1,arg2,......,argn
control-string: 决定如何格式化参数arg1,arg2,....argn
转换规格:
%[-][x][.y]conv
'-' 使得参数左对齐,'x' 最小字段宽度,'.y' 数字中小数点右边的位数,'conv' 数值转换类型。
控制结构
控制语句将改变gawk 程序中命令的执行顺序。
if...else
if(condition)
{commands}
[else
{commands}]
--------------------------
while
while(condition)
{commands}
--------------------------
for
for(init;condition;increment)
{commands}
for(var in array)
{commands}
循环遍历一个名为 array 的关联数组中的所有元素,每次循环,都将 array 的 相应元素的索引值 赋给 var
-------------------------
break
continue
示例:
下面这个示例读取passwd 文件,列出没有口令的用户以及具有重复用户ID 编号的用户,注意使用””输出的字符串信息中排版所使用的空白符,因为默认情况下使用print 输出时除非利用',' 分隔字段( 输出时使用OFS 替代',' 从而将各项分隔开来) ,否则将各个输出项连接起来。
1 gawk < /etc/passwd '
2 BEGIN {
3 uid[void] =""
4 }
5 {
6 dup = 0
7 split($0,field,":")
8 if(field[2] == "")
9 {
10 if(field[5] == "")
11 {
12 print field[1] " has no password."
13 }
14 else
15 {
16 print field[1] " ("field[5]") has no password."
17 }
18 }
19 for(name in uid)
20 {
21 if(uid[name] == field[3] )
22 {
23 print field[1] " has the same UID as " name " /
24 :UID = " uid[num]
25 dup =1
26 }
27 }
28 if(!dup)
29 {
30 uid[field[1]] = field[3]
31 }
32 }'
33
黑体标示部分表征了关联数组的用法,uid 数组使用field[1] 作为数组的下标索引( 在本例中为用户名) ,数组中该索引对应的数组元素的值为field[3]( 在本例中为用户的uid) 。for 循环在每次循环中,将数组uid 的索引值 赋给变量name ,if 判断利用该索引值对应的数组元素来判断是否存在uid 重复。
函数split 利用':' 将当前记录($0 当前记录作为当个变量) 分隔到数组field 中。上面提到的用户名,用户uid 等都是通过引用数组field 的元素得到的。除了利用split 函数以外,也可以利用给FS( 输入字段分隔符) 赋值为”:” 来达到对输入记录分隔开来的目的;只不过split 分隔后利用数组元素来引用相应项( 用户名,uid 等等) ,而利用FS 赋值后可以使用对字段的访问($1 引用用户名, 等) 来引用相应项。下面的两个例子分别使用了上述的两种方式来对输入记录进行处理,其得到的结果相同。
1 gawk < /etc/passwd '
2 BEGIN {
3 }
4 {
5 split($0,field,":")
6 print field[1],field[2],field[3],field[4]
7 }'
8
--------------------
1 gawk < /etc/passwd '
2 BEGIN {
3 FS = ":"
4 }
5 {
6 print $1,$2,$3,$4
7 }'
8
> gawk '{print}' cars
> gawk '/chevy/' cars
> gawk '{print $2,$1}' cars
> gawk '{print $3,$1}' cars
> gawk '/chevy/ {print $3, $1}' cars
> gawk '{ print }' cars
> gawk '{print}' cars
> gawk '/chevy/' cars
> gawk '/chevy/' cars > test.out
> gawk '{print $3,$1}' cars
> gawk '{print $3 $1}' cars
> gawk '{print $3,$1}' cars
> gawk '/chevy/ {print $3,$1}' cars
> gawk '/h/' cars
> gawk '$1 ~ /h/' cars
> gawk '$1 ~ /^h/' cars
> gawk '$2 ~ /^[tm]/ {print $3,$2,"$" $5}' cars
> gawk '$3 ~ /5$/ {print $3,$1,"$" $5}' cars
> gawk '$3 == 1985' cars
> gawk '$5 < 3000' cars
> gawk '$5 <= 3000' cars
> gawk '$1 > 480 {print $2,$3,$4}'
> gawk '$1 > 480 {print $2,$3,$4}' history.out
> gawk '"2000" <= $5 && $5 < "9000"' cars
> gawk '"2000" <= $5 && $5 < "9000"' cars
> gawk '2000 <= $5 && $5 < 9000' cars
> gawk '/volvo/ , /bmw/' cars
> gawk '/volvo/,/bmw/' cars
> gawk '/chevy/,/ford/' cars
> gawk '{print length,$0}' cars | sort -n
> gawk 'length >24 {print NR}' cars
> gawk 'NR == 2 , NR == 4' cars
> gawk 'END {print NR,"cars for sale."}' cars
> gawk 'END {print NR,"cars for sale."} /{print}' cars
> gawk '/mark/ {print}' /etc/passwd
> gawk '/terry/ {print}' /etc/passwd
> history | gawk '{$1 = ">"; print}' | sed -n '/gawk/ p'