awk的基本结构:
awk options ' BEGIN{ print "start" } pattern { commands } END{ print "end" } file
awk语法主要包含三个模块:BEGIN语句块、END语句块和能够使用 模式匹配的通用语句块。这3个部分是可选的,它们中任何一个部分都 可以不出现在脚本中。脚本通常会被包含在单引号或双引号中。
工作原理:
执行BEGIN { commands } 语句块中的语句。
从文件或stdin中读取一行,然后执行pattern { commands }。
重复这个过程,直到文件全部被读取完毕。
- 当读至输入流(input stream)末尾时,执行END { commands } 语 句块。
BEGIN语句块:
BENGIN语句块是再awk开始从输入流读取行之前被执行,只会执行一次。是可选语句块。
诸如变量初始化、打印输出表格的表头等语句通常都可以写 入BEGIN语句块中
END语句块
与BEGIN语句块类似,只会执行一次,是在awk从输入流读取完所有的行之后立即被执行。也是可选数据库。
像打印所有行的分析结果这类汇总信息,都是 在END语句块中实现的常见任务
Pattern块
pattern语句块用于匹配输入数据的模式。如果省略,则 awk
将对所有行进行操作。
这个语句块同样是可 选的。如果不提供该语句块,则默认执行{ print },即打印每一个读 取到的行。
awk对于读取的每一行,都会执行这个语句块。
每读取一行时,它就会检查该行和提供的样式是否匹配。样式本身可以 是正则表达式、条件以及行匹配范围等。如果当前行匹配该样式,则执 行{ }中的语句。 样式是可选的。如果没有提供样式,那么它就会默认所有的行都是匹配 的,并执行{ }中的语句。
{commands}块
awk脚本中的通用命令,是最重要的块。对与前面模式匹配的进行操作,若是没有pattern则,commands对于读入的每一行执行操作。
options
-
-F <分隔符>
或--field-separator=<分隔符>
: 指定输入字段的分隔符,默认是空格。使用这个选项可以指定不同于默认分隔符的字段分隔符。 -
-v <变量名>=<值>
: 设置awk
内部的变量值。可以使用该选项将外部值传递给awk
脚本中的变量。 -
-f <脚本文件>
: 指定一个包含awk
脚本的文件。这样可以在文件中编写较大的awk
脚本,然后通过-f
选项将其加载。 -
-V
或--version
: 显示awk
的版本信息。 -
-h
或--help
: 显示awk
的帮助信息,包括选项和用法示例。
注意点
通常将变量初始化语句(如var=0)以及打印文件头部的 语句放入BEGIN语句块中。若是放在 pattern { commands }语句块中则每行都会被初始化或者打印
awk特殊变量
NR:表示记录数量(number of records),在执行过程中对应 于当前行号。
NF:表示字段数量(number of fields),在执行过程中对应于 当前行的字段数。
$0:这个变量包含执行过程中当前行的文本内容。
$1:这个变量包含第一个字段的文本内容。
$2:这个变量包含第二个字段的文本内容。
$NF:一行中最后一个字段。用print $NF打印一行中最后一个字段,用 $(NF-1)打印 倒数第二个字段
使用例子:
[yuxuanxuan@bastion3 ~]$ echo -e "line1 f2 f3 \nline2 f4 f5 \nline3 f6 f7"|awk '{print "Line_No:"NR,"fields num:"NF,"$0="$0,"$1="$1,"$2="$2,"$3="$NF}'
Line_No:1 fields num:3 $0=line1 f2 f3 $1=line1 $2=f2 $3=f3
Line_No:2 fields num:3 $0=line2 f4 f5 $1=line2 $2=f4 $3=f5
Line_No:3 fields num:3 $0=line3 f6 f7 $1=line3 $2=f6 $3=f7
echo输出三行文本: echo -e "line1 f2 f3 \nline2 f4 f5 \nline3 f6 f7"
awk的pattern部分是{print "Line_No:"NR,"fields num:"NF,"$0="$0,"$1="$1,"$2="$2,"3="NF},对于每行文本都会执行
双引号与单引号的使用
1,awk中print语句中,双引号是被当作拼接使用的:
echo | awk '{ var1="v1"; var2="v2"; var3="v3"; print var1"-"var2"-"var3 ; }'
打印:v1-v2-v3
上面的{}类似一个循环体,对文件中的每一行进行迭代,但是echo只输入一行,所以只打印一次
2,脚本通常被包含在单引号或者双引号中,看具体使用场景而定
例如
[sss@git2 shelltest]$ awk 'BEGIN { i=0 } { i++ } END{ print i}' testfile.txt
5
[sss@git2 shelltest]$ awk "BEGIN { i=0 } { i++ } END{ print i}" testfile.txt
5
[sss@git2 shelltest]$
注意:要是脚本中有awk特殊变量的使用,只能使用单引号
awk -F' +' "{if ($2 > 90){print $1 '\tgood'}else{print $1 '\tbad'} }END{print 'total:',NR}" test.txt
运行报错:
awk: cmd. line:1: {if ( > 90){print '\tgood'}else{print '\tbad'} }END{print 'total:',NR}
awk: cmd. line:1: ^ syntax error
awk: cmd. line:1: {if ( > 90){print '\tgood'}else{print '\tbad'} }END{print 'total:',NR}
awk: cmd. line:1: ^ invalid char ''' in expression
因为其中的双引号中$2会被误认为shell中的变量进行解析,另外'\tgood' '\tbad'也不会被正确解析。需要使用单引号括起,改为如下的形式即可:
awk -F' +' '{if ($2 > 90){print $1 "\tgood"}else{print $1 "\tbad"} }END{print "total:",NR}' test.txt
awk中的if使用
if 语句块
if (表达式 ) {
语句;语句;...
}
属于操作块,if语句若是单个,则不需要{}
awk '{if (condition) {statement1;statement2;statement3;....} }' [input_file]
awk '{if(NR>=2 && NR<=3) print $1}' testfile.txt
选取2,3行打印出来
if else语句块
awk '{ if (condition) {statement1;statement2;statement3;....} else {statement1;statement2;statement3;....} }' [input_file]
if else-if语句块
if(condition1){
command1
}
else if(condition2) {
command2
}
else if(condition3) {
command3
}
.
.
.
else{
commandN
}
awk的三重运算符
条件为真,conmand1语句块将会执行,条件为假,command2将会执行。
(condition) ? Command1:Command2
例子:
awk '{print ($3 <=20)? "Age less than 20: " $2: "Age over 20: " $2}' students.txt
awk 正则匹配
~,!~表示匹配或者不匹配
// 中是模式。
例如:
第二列匹配正则/th/则执行打印 {print $2,$4}
awk '$2 ~ /th/ {print $2,$4}' log.txt
log.txt中每一列只要有匹配re的就打印所有行内容
awk '/re/ ' log.txt
awk for循环的使用
第一种循环体:for (初始化; 条件; 迭代) { # 循环体 }
for (初始化; 条件; 迭代) { # 循环体 }
初始化:初始化一个计数器或变量,通常在循环开始之前执行。
条件:在每次迭代之前检查的条件,如果条件为真,循环会继续执行;如果条件为假,循环会终止。
迭代:在每次迭代之后执行的操作,通常用于递增或递减计数器。
例如,如下例子是依次打印testfile.txt中的每一行的所有字段,NF表示字段数量
awk '{ for(i=1; i<=NF; i++) print $i }' testfile.txt
第二种循环体:for(元素 in 元素列表){#循环体}
比如以下代码:
awk 'BEGIN {
#awk加注释,跟shell一样,#号开头就可以了。
#我们定义一个names的数组,键存的是名字, 值是年龄。
names["Alice"] = 25
names["Bob"] = 30
names["Carol"] = 28
#我们可以循环遍历names数组,并打印元素
for (name in names) {
printf("%s的年龄是%d岁\n", name, names[name])
}
}'
打印
Carol的年龄是28岁
Bob的年龄是30岁
Alice的年龄是25岁