作为一款工具,awk被设计用于数据流。它非常有趣,其原因就在于它可以对列和行进行操作。awk有很多内建的功能,比如数组、函数,这是它和c语言的相同之处。灵活性是awk最大的优势。

  
awk脚本的结构如下:
awk 'BEGIN{print "start"} pattern {commands} END{print "end"}' file
 
    一个awk脚本通常由3部分组成:BEGIN语句块、END语句块和能够使用模式匹配的通用语句块。
这3个部分是可选的,他们中任何一个部分都可以不出现在脚本中。脚本通常会被包含在单引号双引号中:
    awk 'BEGIN{statements}{statements}END{end statements}' file
例如:
    awk 'BEGIN{i=0}{i++}END{print i}' filename
 
工作原理
 
    awk命令的工作方式如下:
(1)执行BEGIN{commands}语句块中的语句。
(2)从文件或stdin中读取一行,然后执行pattern{commands}。重复这个过程,直到文件全部被读取完毕。
(3)当读至输入流末尾时,执行END{commands}语句块。
BEGIN语句块在awk开始从输入流中读取行之前被执行。这是一个可选的语句块,诸如变量初始化、打印输出表格的表头等该语句通常都可以写入BEGIN语句块中。
END语句块和BEGIN语句块类似。END语句块在awk从输入流中读取完所有的行之后即被执行。想打印所有行的分析结果这类汇总信息,都是END语句块中实现的常见任务。它也是一个可选的语句块。
    最重要的部分就是pattern语句块中的通用命令。这个语句块同样是可选的。如果不提供该语句块,则默认执行{print },即打印每一个读取到的行。awk对于读取的每一行,都会执行这个语句块。
    就像一个用来读取行的while循环,在循环体内提供了响应的语句。
    每读取一行时,它就会检查该行和提供的样式是否匹配。样式本身可以使正则表达式、条件、以及行的匹配范围等。如果当前行匹配该样式,则执行{}中的语句。
    样式是可选的。如果没有提供样式,那么他就会默认所有的行都是匹配的,并执行{}中的语句。
    让我们看看下面的例子:
    echo -e "line1\nline2" | awk 'BEGIN{print "start"}{print }END{print "end"}'
start
line1
line2
end
 
    当使用不带参数的print时,它会打印出当前行。关于print,需要记住两件重要的事情:
当print的参数是以逗号进行分隔时,参数打印时则以空格作为定界符;在awk的print语句中,双引号是被当做拼接操作符使用的。
 
   例如:
    echo | awk '{var1="v1";var2="v2";var3="v3";print var1,var2,var3}'
   该语句将按照下面的格式打印变量值:
    v1 v2 v3
   拼接的使用方法:
    echo | awk '{var1="v1";var2="v2";var3="v3";print var1"-"var2"-"var3}'
 
    
1.awk的特殊变量
 
NR:表示记录数量,在执行过程中对应于当前行号。
NF:表示字段数量,在执行过程中对应于当前行的字段数。
$0:这个变量包含执行过程中当前行的文本内容。
$1:这个变量包含第一个字段的文本内容。
$2:这个变量包含第二个字段的文本内容。
 
例如:
echo -e "line1 f2 f3\nlin2 f4 f5\nlin3 f6 f7" | \
awk '{print "line no:"NR",No of fields:"NF,"$0="$0,"$1="$1,"$2="$2,"$3="$3}'
 
line no:1,No of fields:3 $0=line1 f2 f3 $1=line1 $2=f2 $3=f3
line no:2,No of fields:3 $0=lin2 f4 f5 $1=lin2 $2=f4 $3=f5
line no:3,No of fields:3 $0=lin3 f6 f7 $1=lin3 $2=f6 $3=f7
 
我们可以用print $NF打印一行中的最后一个字段,用$(NF-1)打印倒数第二个字段,其他字段一次类推即可。
 
awk的printf()函数的语法和c语言中的同名函数一样。我们也可以用这个函数来代替print。
再来看awk的一些基本用法。
打印每一行的第2和第3个字段:
awk 'print $3,$2' file
要统计文件中的行数,使用下面的命令:
awk 'END{print NR}' file
这里只使用了end语句块。每读一行,awk会将NR更新为对应的行号。每到达最后一行时,NR中的值就是最后一行的行号,于是位于end语句块中的NR就包含了文件的行数。
 
你可以将每一行中第一个字段的值按照下面的方法进行累加:
seq 5 | awk 'BEGIN{sum=0;print "Summation:"}{print $1"+";sum+=1}END{print "==";print sum}'
 
 
2.将外部的变量值传递给awk
 
借助选项-v,我们可以将外部值传递给awk:
VAR=10000
echo | awk -v variable=$VAR '{print variable}'
 
还有另一种灵活的方法可以将多个外部变量传递给awk,例如:
var1="variable1";var2="variable2"
echo | awk '{print v1,v2}' v1=$var1,v2=$var2
Variable1 Variable2
当输入来自于文件而非标准输入时,使用:
awk '{print v1,v2}' v1=$var1 v2=$var2 filename
在上面的方法中,变量之间用个空格分隔,以键值对的形式作为awk的命名行参数。
 
3.用getline读取行
 
通常,grep默认读取一个文件的所有行。如果只想读取某一行,可以使用getline函数。
有时候,我们需要从BEGIN语句块中读取第一行。
语法:getline var
变量var就包含了特定的行的内容。
如果调用不带参数的getline,我们可以用$0、$1、$2访问文本行的内容。
例如:
seq 5 | awk 'BEGIN{getline;print "Read ahead first line",$0}{print $0}'
 
4.用样式对awk处理的行进行过滤
 
我们可以为需要处理的行指定一些条件,例如:
awk 'NR<5' #行号小于5的行
awk 'NR==1,NR==4' #行号在1到5之间的行
awk '/linux/' #包含样式linux行(可以用正则表达式来指定样式)
awk '!/linux/' #不包含样式linux的行
 
5.设置字段定界符
 
默认的字段定界符是空格。我们也可以用-F "delimiter" 明确指定给一个定界符:
awk -F: 'print $NF' /etc/passwd
或者
awk 'BEGIN{FS=":"}{print $NF}' /etc/passwd
在BEGIN语句块中则可以用OFS="delimiter" 设置输出字段的定界符。
 
6.从awk中读取命令输出
 
在下面的代码中,echo 会生成一个空白行。变量cmdout包含命令grep root /etc/passwd的输出,然后打印包含root的行:
将command的输出读入变量output的语法如下:
"command" | getline output;
例如:
echo | awk '{"grep root /etc/passwd" | getline cmdout ; print cmdout}'
root:x:0:0:root:/root:/bin/bash
通过使用getline,能够将外部的shell命令的输出读入变量cmdout。
awk支持以文本作为索引的关联数组。
 
7.在awk中使用循环
 
在awk中可以使用for循环,格式如下:
for(i=0;i<10;i++){print $i}
或者
for(i in array){print array[i];}
awk有很多内建的字符串控制函数,让我们认识一下其中部分函数。
length(string):返回字符串的长度。
index(string,search_string):返回search_string在字符串中出现的位置。
split(string,array,delimiter):用定界符生成一个字符串列表,并将该列表存入数组。
substr(string,star-position,end-position):在字符串中用字符起止偏移量生成子串,并返回该子串。
sub(regex,replacement_str,string):将正则表达式匹配到的第一处内容替换成replacment_str。
gsub(regex,replacment_str,string):和sub()类似。不过该函数会替换正则表达式匹配到的所有内容。
match(regex,string):检查正则表达式是否能够匹配字符串。如果能够匹配,返回非0值;否则,返回0。match()有两个相关的特殊变量,分别是RSTART和RLENGTH。变量RSTART包含正则表达式所匹配内容的起始位置,而变量RLENGTH包含正则表达式所匹配内容的长度。