[Linux Shell学习系列十四]sed和awk-5.awk基础
D29
我们可以将awk的起源追溯到sed和grep,并且经由sed和greap再追溯到ed(最初的Unix行编辑器)。接下来会发现awk和sed有很多相似之处。
1. awk简介
awk被设计用于文本处理,并通常被用作数据提取和报告工具的解释性程序语言。
awk为处理文件提供了更普遍的计算模型。
awk中的程序与大部分其他语言的程序不同:awk程序是数据驱动的,描述你要处理的数据及找到后要做什么;其他大部分语言则是过程化的,必须详细的描述程序每一步要做什么。
awk程序的典型示例是将数据转换成格式化的报表;
另一个例子是由独立的数据项和数据检索程序组成的数据处理应用程序:数据项是以结构化方式记录的数据;数据检索是从文件中提取数据并形成报告的过程。
所有这些操作的关键是数据拥有某种结果,即数据要按照一定的分类方式进行归类。
awk不能处理非文本文件,比如二进制执行文件等(其需要使用类似emacs中的hexlmode等二进制编辑器)。
和sed类似,awk的基本功能也是搜索文件中包含某些模式的行(或其他文本单元)。当某行匹配一个模式时,awk就在那一行执行指定的操作。awk持续的用这种方式处理输入的行,直到文件结尾。
2. awk基本语法
awk程序由一系列awk指令组成。一个awk指令由一个模式pattern后跟一个动作action组成。动作在花括号{}内,用来与模式分离。awk指令之间通常用换行符分隔。
语法:pattern {action}
运行awk的方法有两种:
1)awk程序很短时:awk [OPTIONS] [--] program-text file ...
2)awk程序很长时,放在文件中更方便:awk [OPTION] -f program-file [--] file ...
可以看到,一个awk命令行是由选项、awk程序文件或指令、输入文件名组成的。如果没有指定输入文件名或指定为-,则从标准输入中获取输入。
常用的选项如下:
选项 | 用法 | 说明 |
-F | -F fs | 指定用于输入数据的列分隔符fs |
-v | -v var=value | 在awk程序执行前指定一个值给value给变量var。这些变量值用于awk程序的BEGIN块。 |
-f | -f program-file | 指定一个awk程序文件,代替在命令行指定awk指令。 |
-- | -- |
根据POSIX参数解析约定,此选项代表命令行选项的结束。 例如,利用这个选项可以指定以“-”开头的输入文件,否则它将被解析为一个命令行选项。 |
3. 第一个awk命令
#awk会在输入文件info.txt的每一行上按顺序执行它的print命令 #print命令不带任何参数时,打印当前行的所有内容 $ awk '{ print }' info.txt Linux - Sysadmin Database - Oracle, MySQL etc. Security - Firewall, Network, Online Security etc. Cool - Websites #print命令+变量$0,$0代表当前的一整行 $ awk '{ print $0 }' info.txt Linux - Sysadmin Database - Oracle, MySQL etc. Security - Firewall, Network, Online Security etc. Cool - Websites #print命令+固定字符串,则输入文件有几行,就打印几个固定字符串 $ awk '{ print "hello" }' info.txt hello hello hello hello
4. 使用awk打印指定的列
#不指定列分隔符的情况下,awk默认使用空白作为列分隔符 $ awk '{ print $1 }' info.txt Linux Database Security Cool #使用-F选项指定符号-为列分隔符 $ awk -F'-' '{ print $2 }' info.txt Sysadmin Oracle, MySQL etc. Firewall, Network, Online Security etc. Websites
5. 从awk程序文件读取awk指令
$ cat secCol.awk BEGIN { FS="-" } { print $2 }
#使用-f选项指定awk程序文件 $ awk -f secCol.awk info.txt Sysadmin Oracle, MySQL etc. Firewall, Network, Online Security etc. Websites
6. awk的BEGIN和END块
正常情况下,awk对每一个输入行都会执行一次awk程序代码。对于需要在awk开始处理输入文件中的文本前执行一些初始化代码时,可以定义一个BEGIN块,用于初始化FS变量(列分隔符)、打印标题、或是初始化其他全局变量。
7. awk中使用正则表达式
awk可以使用正则表达式来有选择地执行一个独立的代码块,但正则表达式必须放在斜线内,与sed类似,或者用操作符~/!~指定任意列或变量匹配/不匹配一个正则表达式。
#包含字符串admin的行 $ awk '/admin/{ print }' info.txt Linux - Sysadmin $ awk -F'-' '/admin/{ print $1 }' info.txt Linux #第二列匹配-的行 $ awk '$2 ~ /-/ { print $1 }' info.txt Linux Database Security Cool
D30
8. awk的表达式和块
awk有选择的执行代码块的方法:把任意类型的布尔表达式放在代码块之前来控制特定的块什么时候可以执行,即只有当代码块前面的布尔表达式值为真时,awk才会执行此代码块。
#/etc/passwd中第一列等于root的所有行的第三列 $ awk 'BEGIN { FS=":" } $1 == "root" { print $3 }' /etc/passwd 0
语法:变量 操作符 正则表达式或字符串
比较操作符:==, <, >, <=, >=, !=
另外:~(匹配), !=(不匹配)
$ cat info.txt Linux - Sysadmin Database - Oracle, MySQL etc. Security - Firewall, Network, Online Security etc. Cool - Websites #以-分隔的第二列匹配字符串etc的所有行的第一列 $ awk 'BEGIN { FS="-" } $2 ~ "etc" { print $1 }' info.txt Database Security
表达式是awk程序的基本组成部分。一个表达式可以对你打印的、测试的或传递给一个函数的内容进行求值,也可以使用赋值操作符给一个变量或一个列赋予一个新的值。
其他大多数类型的语句都包含一个或多个指定数据在哪处理的表达式。和其他语言一样,awk的表达式包含变量、数组引用、常量和函数调用,此外还有它们与各种操作符的组合。
9.awk的条件语句
awk还提供了很好的类似于C语言的if语句。
#与上一节中示例功能一样,但具体的执行方式不同 $ awk 'BEGIN { FS="-" } { if ( $2 ~ "etc" ) { print $1 } }' info.txt Database Security
上一节中的布尔表达式($2 ~ "etc" )放在块的外部;本例中块({ if ( $2 ~ "etc" ) { print $1 } })对每一个输入行都要执行一次,而通过使用if语句(if ( $2 ~ "etc" ) ),有选择的执行print指令。可根据需要选择。
if-else语句是awk的决策语句。
语法:if (condition) then-body [else else-body]
condition:条件,控制语句的剩余部分要做什么的一个表达式;为真时then-body被执行,为假时else-body被执行;为0或空字符串时为假,否则为真;
else部分是可选的。
awk也可以使用布尔操作符||逻辑或和&&逻辑与来创建更复杂的布尔表达式。
#打印以-分隔,第一列匹配Linux或第二列匹配Network的行 $ awk 'BEGIN { FS="-" } ( $1 ~ "Linux" || $2 ~ "Network" ) { print }' info.txt Linux - Sysadmin Security - Firewall, Network, Online Security etc.
10. awk中的变量和操作符
awk允许我们执行整数和浮点数运算。
$ cat mail.txt From: A@a.com To: Test subject: test #末尾有空行 #awk程序文件 #BIGIN块中变量x初始化为0 #每次遇到空白行时,对变量x进行累加 #处理所有行后,END块执行,输出最后的总结 $ cat foundBlankLine.awk BEGIN { x=0 } /^$/ { x=x+1 } END { print "Found " x " blank lines." } #执行 $ awk -f foundBlankLine.awk mail.txt Found 2 blank lines.
awk中,变量上执行数学运算时,只要变量包含有效的数字串,awk会自动的处理字符串到数字的转换步骤。如:
#变量x初始化为字符串,仍然可以用数学运算表达式将其值加1 $ awk 'BEGIN { x="3.5" } END { print x+1 }' info.txt 4.5
而Bash不支持浮点运算,并且Bash执行任何数学运算时,都要将算式用"$(( ))"括起来。而是用awk,操作是全自动的,且代码好看而整洁。
$ cat math.txt a 2 b 13 c 5 #计算第二列的平方加1 $ awk '{ print ( $2^2 ) + 1 }' math.txt 5 170 26
如果指定的变量不包含有效的数字,在数学表达式中用于计算,awk会把这个变量作为数字0处理。
#math.txt中第一列为字母,计算其平方 $ awk '{ print ( $1^2 ) }' math.txt 0 0 0
关于awk的另一个好处是它含有完整的算术运算符:标准的加减乘除运算符,指数运算符^,模运算符%,预增加运算符++i,预减少运算符--i,后增加运算符i++,后减少运算符i--,加赋值运算符+=,减赋值-=,乘赋值*=,除赋值/=等。
11. awk中的特殊变量
awk特殊变量:一类用来调整awk如何工作,另一类用于收集与输入相关的有用的信息。
FS变量:设置awk在列之间查找的字符序列。可以是单个字符或正则表达式或任意长度的字符模式。
#以一个或多个制表符分隔 FS="\t+" #由空白分隔(一个或多个空格,制表符)
#这个其实就是默认情况:FS设为一个空格字符,awk就会解释为一个或多个空格,制表符
FS="[[:space:]+]"
下面两个变量通常我们不会改写它的值,而是读取它们的值来获得与输入内容相关的有用信息。
1)NF变量:用于记录列的数量。
$ cat info.txt Linux - Sysadmin Database - Oracle, MySQL etc. Security - Firewall, Network, Online Security etc. Cool - Websites #只打印列数是3的行 $ awk 'NF == 3 { print }' info.txt Linux - Sysadmin Cool - Websites #也可以在条件语句中使用NF $ awk '{ if ( NF < 4 ) { print $1":"$3 } }' info.txt Linux:Sysadmin Cool:Websites
2)NR变量:用于记录当前记录的数量(awk将第一条记录的该值为数字1)。
$ cat info.txt Linux - Sysadmin Database - Oracle, MySQL etc. Security - Firewall, Network, Online Security etc. Cool - Websites #打印第2行以后的记录,并添加行号 $ awk '{ if ( NR > 2 ) { print NR".\t"$0 } }' info.txt 3. Security - Firewall, Network, Online Security etc. 4. Cool - Websites
12. awk中的循环结构
awk中有for、while、do-while循环。
1)while循环
语法:
while (condition)
body
body:代表任意awk语句;
condition:控制循环保持运行多久的表达式。
while循环的过程:测试condition,为真则执行body中语句,全部执行一次后,再次测试condition,不断重复这一过程,直到condition不再为真;如果condition一开始就为假,则body不会被执行,awk直接处理循环后面的语句。
$ cat info.txt Linux - Sysadmin Database - Oracle, MySQL etc. Security - Firewall, Network, Online Security etc. Cool - Websites #输出每行记录的前三列,且输出一列占一行 $ awk '{ i=1; while ( i <= 3 ) { print $i; i++ }}' info.txt Linux - Sysadmin Database - Oracle, Security - Firewall, Cool - Websites
2)do-while循环
do-while循环是while循环的变体。
语法:
do
body
while (condition)
do循环先执行body一次,然后只要condition为真就重复执行body。因此body至少会被执行一次。
3)for循环
for循环可以更方便的记录循环次数。
语法:
for ( init; condition; increment )
body
init、condition、increment部分是任意的awk表达式,而body表示任意awk语句。
for循环从执行init开始,如果condition为真,就重复执行body和increment。
#与前面while循环示例完成相同工作 #打印每行的前三列且每列占一行 $ awk '{ for ( i=1; i<=3; i++ ) print $i }' info.txt Linux - Sysadmin Database - Oracle, Security - Firewall, Cool - Websites
13. awk中的数组
在awk下,通常数组索引从1开始,而不是从0开始。
1)数组定义及赋值:
myarr[1]="one" #myarr被创建且第一个元素为one;
myarr[2]="123" #第二个元素为123。
2)迭代数组中的元素:
for ( x in myarr ){
print myarr[x]
}
上述代码将输出myarr中的每一个元素。
注:此处x是索引不是值
但是当awk循环数组索引时,并不遵循特定的顺序。即我们不能确定上面代码的输出是:
one
123
还是:
123
one
3)awk的数组不需要有连续的数字索引序列,如:我们可以定义myarr[1]和myarr[100],而不定义索引为2~99的数组元素。
4)awk的数组除了支持数字索引,也支持字符串索引,如:
myarr["name"]="Tom"
以上特性使awk数组便于使用,但有时可能引起混乱。
为了便于管理,awk提供了:
1)删除awk数组中的元素:
delete myarr[1]
2)查看指定的数组索引是否存在:
1 in myarr
示例:
$ cat info.txt Linux - Sysadmin Database - Oracle, MySQL etc. Security - Firewall, Network, Online Security etc. Cool - Websites #在BEGIN块中定义数组myarr,两个元素赋值; #通过if判断行号为索引的数组元素是否存在,存在的话打印该行 $ awk 'BEGIN { myarr[1]=1; myarr[2]=3 } { if ( NR in myarr) { print NR". "$0 }}' info.txt 1. Linux - Sysadmin 2. Database - Oracle, MySQL etc.
本节结束