1. AWK 工作流程
这一章节中,我们将解释 AWK 是如何工作的。 要想成为 AWK 专家,你必须得了解其内部工作的原理。 AWK 执行的流程非常简单:读( Read )、执 行( Execute )与重复( Repeat )。下面的流程图描述出了 AWK 的工作流程:
完整的示例
' BEGIN {awk-commands-begin} {awk-commands-body} END {awk-commands-end} '
-
单引号包围的是整个 awk 程序。 单引号是必须存在的!!!!
双引号表示字符串,下面示例中有 -
其中 BEGIN 和END
可以省略
-
body部分会按行重复执行
读(Read):
AWK 从输入流(文件、管道或者标准输入)中读入一行然后将其存入内存中。执行(Execute):
对于每一行输入,所有的 AWK 命令按顺执行。 默认情况下,AWK 命令是针对于每一行输入,但是我们可以将其限制在指定的模式中。重复(Repeate):
一直重复上述两个过程直到文件结束。
开始块(BEGIN block)
开始块的语法格式如下所示:
BEGIN {awk-commands}
顾名思义,开始块就是在程序启动的时候执行的代码部分,并且它在整个过程中只执行一次
。 一般情况下,我们在开始块中初始化一些变量。BEGIN 是 AWK 的关键字,因此它必须是大写的。 不过,请注意,开始块部分是可选的,你的程序可以没有开始块部分。
主体块(Body Block)
主体部分的语法要求如下:
/pattern/ {awk-commands}
对于每一个输入的行都会执行一次主体部分的命令。默认情况下,对于输入的每一行,AWK 都会很执行命令。但是,我们可以将其限定在指定的模式中。 注意,在主体块部分没有关键字存在。
pattern
一般是过滤条件 ,例如下面的 NR==1等- {} 及里面的内容是可选的
结束块(END Block)
下面是结束块的语法格式:
END {awk-commands}
结束块是在程序结束时执行的代码。 END 也是 AWK 的关键字,它也必须大写。 与开始块相似,结束块也是可选的。并且它在整个过程中只执行一次
1.1 示例
先创建一个名为 marks.txt 的文件。其中包括序列号、学生名字、课程名称与所得分数。
1) Amit Physics 80
2) Rahul Maths 90
3) Shyam Biology 87
4) Kedar English 85
5) Hari History 89
接下来,我们将使用 AWK 脚本来显示输出文件中的内容,同时输出表头信息。
[jerry]$ awk 'BEGIN{printf "Sr No\tName\tSub\tMarks\n"} {print}' marks.txt
执行上面的代码后,将会输出如下的结果:
Sr No Name Sub Marks
1) Amit Physics 80
2) Rahul Maths 90
3) Shyam Biology 87
4) Kedar English 85
5) Hari History 89
程序启动时,AWK 在开始块中输出表头信息。在主体块中,AWK 每读入一行就将读入的内容输出至标准输出流中,一直到整个文件被全部读入为止。
2. AWK 内置变量
AWK 提供了一些内置变量。 它们在你写 AWK 脚本的时候起着很重要的作用。 这一章节中将会展示如何使用这些内置变量。
2.1 ARGC
ARGC 表示在命令行提供的参数的个数。
[jerry]$ awk 'BEGIN {print "Arguments =", ARGC}' One Two Three Four
执行上面的命令可以得到如下的结果:
Arguments = 5
程序哪儿出毛病了吗?为什么只输入四个参数而 AWK 却显示输入的参数个数的五呢? 看完下面这个例子,你就会明白的。
2.2 ARGV
这个变量表示存储命令行输入参数的数组。数组的有效索引是从 0 到 ARGC-1。
[jerry]$ awk 'BEGIN { for (i = 0; i < ARGC - 1; ++i)
{ printf "ARGV[%d] = %s\n", i, ARGV[i] }
}' one two three four
执行上面的命令可以得到如下的结果:
ARGV[0] = awk
ARGV[1] = one
ARGV[2] = two
ARGV[3] = three
2.3 CONVFMT
此变量表示数据转换为字符串的格式,其默认值为 %.6g。
[jerry]$ awk 'BEGIN { print "Conversion Format =", CONVFMT }'
执行上面的命令可以得到如下的结果:
Conversion Format = %.6g
2.4 ENVIRON
此变量是与环境变量相关的关联数组变量。
[jerry]$ awk 'BEGIN { print ENVIRON["USER"] }'
执行上面的命令可以得到如下的结果:
jerry
可以使用 GNU/Linux 系统中的 env 命令查询其它环境变量的名字。
2.5 FILENAME
此变量表示当前文件名称。
[jerry]$ awk 'END {print FILENAME}' marks.txt
执行上面的命令可以得到如下的结果:
marks.txt
值得注意的是在开始块中FILENAME是未定义的。
2.6 NF
此变量表示当前输入记录的列数。例如,下面这个例子只输出超过两个列的行:
[jerry]$ echo -e "One Two\nOne Two Three\nOne Two Three Four" | awk 'NF > 2'
等价于 ,默认是打印全部:
[jerry]$ echo -e "One Two\nOne Two Three\nOne Two Three Four" | awk 'NF > 2 { print}'
执行上面的命令可以得到如下的结果:
One Two Three
One Two Three Four
2.7 NR(number of row)
此变量表示当前记录的行号。(译注:该变量类似一个计数器,统计记录的数量)。下面例子会输出读入的前三行(NR<3)。
[jerry]$ echo -e "One Two\nOne Two Three\nOne Two Three Four" | awk 'NR < 3 { print}'
执行上面的命令可以得到如下的结果:
One Two
One Two Three
2.8 FNR
该变量与 NR 类似,不过它是相对于当前文件而言的。此变量在处理多个文件输入时有重要的作用。每当从新的文件中读入时 FNR 都会被重新设置为 0。
2.9 FS
此变量表示输入
的数据域之间的分隔符
,其默认值是空格。 你可以使用 -F 命令行选项改变它的默认值。
[jerry]$ awk 'BEGIN {print "FS = " FS}' | cat -vte
执行上面的命令可以得到如下的结果:
FS = $
cat -vte 此命令会以可视方式显示所有特殊字符(包括制表符、换页符等),并在行尾显示"$"符号。
2.10 RS (row separate)
此变量表示输入
记录的分割符
,其默认值为换行符。
[jerry]$ awk ‘BEGIN {print "RS = " RS}’ | cat -vte
执行上面的命令可以得到如下的结果:
RS = $
$
2.11 OFS
此变量表示输出
域之间的分割符
,其默认为空格。
[jerry]$ awk 'BEGIN {print "OFS = " OFS}' | cat -vte
执行上面的命令可以得到如下的结果:
OFS = $
2.12 ORS (output row separate)
此变量表示输出
记录(行)之间的分割符
,其默认值是换行符。
[jerry]$ awk 'BEGIN {print "ORS = " ORS}' | cat -vte
执行上面的命令可以得到如下的结果:
ORS = $
$
2.13 $0:当前处理行的整行内容
2.14 $n:当前处理行的第 n 个字段(第 n 列)
3. 运算符
与其它编程语言一样,AWK 也提供了大量的操作符。这一章节中,我们将结合例子介绍 AWK 操作符的使用方法:
完整参见 AWK 操作符
3.1 算术运算符
AWK 支持如下的算术运算符:
3.1.1加法运算符
加法运算由符号 + 表示,它求得两个或者多个数字的和。下面是一个使用示例:
[jerry]$ awk 'BEGIN { a = 50; b = 20; print "(a + b) = ", (a + b) }'
执行上面的命令可以得到如下的结果:
(a + b) = 70
4. AWK 正则表达式
4.1 点(Dot)
点字符(.)可以匹配除了行结束字符的所有字符,只能出现一次,且必须出现一次
。比如下面的便子就可以匹配 fin, fun, fan 等等。
echo -e "cat\nbat\nfun\nfin\nfn\nfaan" | awk '/f.n/'
示例中的-e是为了构建换行的。
执行上面命令可以得到如下结果:
fun
fin
4.2 行开始
行开始符(^)匹配一行的开始。下面的示例将输出所有以字符串 The 开始的行。
[jerry]$ echo -e "This\nThat\nThere\nTheir\nthese" | awk '/^The/'
执行上面的命令可以得到如下结果:
There
Their
4.3 行结束
行结束符($)匹配一行的结束。下面的例子中将输出所有以字符 n 结束的行:
> [jerry]$ echo -e "knife\nknow\nfun\nfin\nfan\nnine" | awk '/n$/'
执行上面的命令可以得到如下结果:
fun
fin
fan
4.4 匹配字符集
匹配字符集用于匹配集合
(由方括号
表示)中的一个字符
。如下例子中,匹配 Call 与 Tall 而不会匹配 Ball。
[jerry]$ echo -e "Call\nTall\nBall" | awk '/[CT]all/'
执行上面的命令可以得到如下结果:
Call
Tall
4.5 排除集
正则匹配时会排除集合中的字符。如下例子中只会输出 Ball。
[jerry]$ echo -e "Call\nTall\nBall" | awk '/[^CT]all/'
执行上面的命令可以得到如下结果:
Ball
或
竖线(|)允许正则表达式实现逻辑或运算. 下面例子将会输出 Ball 与 Call 。
[jerry]$ echo -e "Call\nTall\nBall\nSmall\nShall" | awk '/Call|Ball/'
执行上面的命令可以得到如下结果:
Call
Ball
4.6?最多出现一次
该符号( ?)前面
的单个字符
出现0次或者出现一次。如下示例匹配 Colour 与 Color。 使用 ? 使得 u 变成了可选字符 。
[jerry]$ echo -e "Colour\nColor" | awk '/Colou?r/'
执行上面的命令可以得到如下结果:
Colour
Color
4.7 出现零次或多次
该符号(*) 允许其前面
的单个字符
出现多次或者0次。如下示例将匹配 ca,cat, catt 等等。
[jerry]$ echo -e "ca\ncat\ncatt" | awk '/cat*/'
执行上面的命令可以得到如下结果:
ca
cat
catt
4.8 出现一次或多次
该符号(+)使得其前的字符出现一次或者多次。下面的例子会匹配一个 2 或者多个连续的 2。
[jerry]$ echo -e "111\n22\n123\n234\n456\n222" | awk '/2+/'
执行上面的命令可以得到如下结果:
22
123
234
222
4.9 分组
括号用于分组而字符 | 用于提供多种选择。如下的正则表达式会匹配所有包含 Apple Juice 或 Aplle Cake 的行。
与前面 [] 区别在于,[]是取单个字符,而这里是取一个字符串
[jerry]$ echo -e "Apple Juice\nApple Pie\nApple Tart\nApple Cake" | awk '/Apple (Juice|Cake)/'
执行上面的命令可以得到如下结果:
Apple Juice
Apple Cake
5. 控制语句
5.1 IF 语句
条件语句测试条件然后根据条件选择执行相应的动作。下面是条件语句的语法:
if (condition)
action
也可以使用花括号来执行一组操作:
if (condition)
{
action-1
action-1
.
.
action-n
}
下面的例子判断数字是奇数还是偶数:
[jerry]$ awk 'BEGIN {num = 10; if (num % 2 == 0) printf "%d is even number.\n", num }'
执行上面的命令可以得到如下的结果:
10 is even number.
5.2 IF - ELSE 语句
if-else语句中允许在条件为假时执行另外一组的动作。下面为 if-else 的语法格式:
if (condition)
action-1
else
action-2
其中,条件为真时执行 action-1,条件为假时执行 action-2。下面是使用该语句判断数字是否为偶数的例子:
[jerry]$ awk 'BEGIN {num = 11;
if (num % 2 == 0) printf "%d is even number.\n", num;
else printf "%d is odd number.\n", num
}'
执行上面的操作可以得到如下的结果:
11 is odd number.
5.3 if-else-if 梯
我们可以很轻松地使用多个 if-else 语句构造 if-else-if 梯从而实现多个条件的判断。示例如下:
[jerry]$ awk 'BEGIN {
a=30;
if (a==10)
print "a = 10";
else if (a == 20)
print "a = 20";
else if (a == 30)
print "a = 30";
}'
执行上面的命令可以得到如下的结果:
a = 30
6. AWK 循环
6.1 For
For 循环的语法如下:
for (initialisation; condition; increment/decrement)
action
for 语句首先执行初始化动作( initialisation ),然后再检查条件( condition )。如果条件为真,则执行动作( action ),然后执行递增( increment )或者递减( decrement )操作。只要条件为真循环就会一直执行。每次循环结束都会进条件检查,若条件为假则结束 循环。下面的例子使用 For 循环输出数字 1 至 5:
[jerry]$ awk 'BEGIN { for (i = 1; i <= 5; ++i) print i }'
执行上面的命令可以得到如下结果:
1
2
3
4
5
6.2 While
While 循环会一直执行动作直到逻辑条件为假为止。其使用方法如下:
while (condition)
action
AWK 首先检查条件是否为真,若条件为真则执行动作。此过程一直重复直到条件为假时,则停止。下面是使用 While 循环输出数字 1 到 5 的例子:
[jerry]$ awk 'BEGIN {i = 1; while (i < 6) { print i; ++i } }'
执行上面的命令可以得到如下的结果:
1
2
3
4
5
6.3 Do-While
Do-While 循环与 While 循环相似,但是 Do-While 的条件测试放到了循环的尾部。下面是 do-while 的语法:
do
action
while (condition)
在 do-while 循环中,无论条件是真是假,循环语句至少执行一次,执行后检查条件真假。下面是使用 do-While 循环输出数字 1 到 5 的例子:
[jerry]$ awk 'BEGIN {i = 1; do { print i; ++i } while (i < 6) }'
执行上面的命令可以得到如下的结果:
1
2
3
4
5
6.4 Break
顾名思义,break 用以结束循环
过程。在下面的示例子中,当计算的和大于 50 的时候使用 break 结束循环过程:
[jerry]$ awk 'BEGIN {
sum = 0; for (i = 0; i < 20; ++i) {
sum += i; if (sum > 50) break; else print "Sum =", sum
}
}'
执行上面的命令可以得到如下的结果:
Sum = 0
Sum = 1
Sum = 3
Sum = 6
Sum = 10
Sum = 15
Sum = 21
Sum = 28
Sum = 36
Sum = 45
6.5 Continue
Continue 语句用于在循环体内部结束本次循环
,从而直接进入下一次循环迭代。当我们希望跳过循环中某处数据处理时就会用到 Continue。下面的例子输出 1 到 20 之间的偶数:
[jerry]$ awk 'BEGIN {for (i = 1; i <= 20; ++i) {if (i % 2 == 0) print i ; else continue} }'
执行上面的命令可以得到如下的结果:
2
4
6
8
10
12
14
16
18
20
6.6 Exit
Exit 用于结束脚本
程序的执行。该函数接受一个整数作为参数表示 AWK 进程结束状态。 如果没有提供该参数,其默认状态为 0 。下面例子中当和大于 50 时结束 AWK 程序。
[jerry]$ awk 'BEGIN {
sum = 0; for (i = 0; i < 20; ++i) {
sum += i; if (sum > 50) exit(10); else print "Sum =", sum
}
}'
执行上面的命令可以得到如下的结果:
Sum = 0
Sum = 1
Sum = 3
Sum = 6
Sum = 10
Sum = 15
Sum = 21
Sum = 28
Sum = 36
Sum = 45
让我们检查一下脚本执行后的返回状态:
[jerry]$ echo $?
执行上面的命令可以得到如下的结果:
10
参考
https://www.w3cschool.cn/awk/27bx1k8p.html