awk
字数: 8072 字 时长: 30 分钟
awk
是一种强大的文本处理工具和编程语言,它可以逐行处理文本文件,并根据用户定义的模式和动作对文本进行操作。awk
处理文件时,将文件视为由记录(通常是行)和字段(通常是由空格或制表符分隔的列)组成。它可以方便地提取、过滤、转换和计算文本数据。
awk
的特点:
- 功能强大:
awk
是一种功能强大的文本处理工具,同时也是一种编程语言,用于在 Linux/Unix 系统中对文本和数据进行处理。它可以处理来自标准输入、一个或多个文件,或其他命令的输出。 - 支持复杂操作:
awk
支持用户自定义函数、动态正则表达式等高级功能,能够实现复杂的文本处理任务。 - 面向记录和字段:
awk
将输入的内容视为一系列的记录(行),每条记录又进一步分割为多个字段(列)。awk
通过内置变量NR
(记录数)和NF
(字段数)来管理和调用这些记录和字段的信息。
awk
的工作原理:
当 awk
扫描一个文本文件时,它会逐行读取内容,将每一行视为一条记录,然后根据指定的分隔符(默认为空白字符,如空格或制表符)将记录分割为多个字段。awk
通过内置变量和模式匹配来处理这些记录和字段。
基本语法
awk
的基本语法如下:
awk [options] 'Pattern {Action}' file
常见选项 | 含义 |
---|---|
-F <fs> | 指定以 fs 作为输入行的分隔符,fs 是一个字符串或正则表达式。awk 命令默认分隔符为空格或者制表符。 |
f <file> | 从脚本文件中读取 awk 脚本指令,以取代直接在命令行中输入指令。 |
-v <var>=<value> | 在执行处理过程之前,设置一个变量并赋值。 |
模式-动作
awk
的核心是「模式-动作」结构,这是 awk
程序的基本单元。每条规则由这两部分组成。
模式(Pattern):
模式是一个条件,用于决定是否对当前行执行某个动作。模式可以是:
- 正则表达式:如
/pattern/
,用于匹配包含特定字符串或模式的行。例如/apple/
会匹配任何包含apple
的行。 - 关系表达式:使用比较运算符,如
$2 > 10
会匹配第二字段的值大于 10 的行。 - 逻辑表达式:可以使用逻辑运算符
&&
(与)、||
(或)和!
(非)组合多个条件,如$1 == "John" && $3 > 20
会匹配第一字段为 John 且第三字段大于 20 的行。 - 特殊模式:如
BEGIN
和END
,BEGIN
模式在处理任何行之前执行,通常用于初始化变量;END
模式在处理完所有行之后执行,常用于计算汇总结果。
动作(Action):
动作是当模式匹配成功时执行的操作,由一系列的 awk
语句组成,用花括号 {}
括起来。常见的动作包括:
- 打印操作:使用
print
或printf
语句输出信息。例如awk '{ print $1 }' file.txt
,会打印 file.txt 中每一行的第一个字段。 - 赋值操作:可以给变量赋值,例如 { sum += $2 } 会将第二字段的值累加到 sum 变量中。
- 条件和循环语句:可以使用 if、else、while、for 等语句进行更复杂的逻辑控制。例如
awk '{ if ($2 > 50) print $1 }' file.txt
会打印第二字段大于 50 的行的第一个字段。
字段分割
awk
的核心特性之一就是自动字段分割。
awk
会自动将每一行的文本分割成多个字段,并为每个字段分配一个变量(如 $1
、$2
等)。这种自动字段分割是 awk
强大的原因之一,因为它极大地简化了对结构化数据的处理。
当 awk
逐行读取输入文件时,它会根据预定义的字段分隔符(默认是空格或制表符)将每一行分割成多个字段。每个字段被自动分配到一个字段变量中:
$1
:表示当前行的第一个字段。$2
:表示当前行的第二个字段。$3
:表示当前行的第三个字段。$n
:表示当前行的第 n 个字段。$0
:表示当前行的完整内容(所有字段的组合)。
默认情况下,awk
使用空格或制表符作为字段分隔符。这意味着连续的空格或制表符被视为一个分隔符。
假设有一个文件 data.txt,内容如下:
Alice 30 New York
Bob,25,Los Angeles
Charlie 35 Chicago
David:40:San Francisco
Eve 22 Boston
Frank 33 Seattle
Grace,28,Phoenix
默认字段分隔符
提取第 1 列和第 3 列(默认以空格或制表符分隔)。
shell[root@localhost ~]# awk '{print $1, $3}' data.txt Alice New Bob,25,Los Charlie Chicago David:40:San Eve Boston Frank Seattle Grace,28,Phoenix
在默认情况下,
awk
使用空格或制表符作为字段分隔符。如果某行中没有这些分隔符(例如Bob,25,Los Angeles
或David:40:San Francisco
),awk
会将整行视为一个字段($1
),而$2
和$3
则是空的。自定义字段分隔符
可以通过
-F
选项或在程序中设置FS
变量来自定义字段分隔符,例如使用逗号作为字段分隔符:shell[root@localhost ~]# awk -F, '{print $2,$3}' data.txt 25 Los Angeles 28 Phoenix
因为指定字段分隔符为逗号,所以没有其他没有逗号的行不存在第二字段和第三字段。
多种分隔符
awk 也支持不同分隔符,像这样文件中包含多种分隔符(例如空格、逗号、冒号),可以使用正则表达式作为字段分隔符:
shell[root@localhost ~]# awk -F'[ ,:]+' '{print $1, $3}' data.txt Alice New Bob Los Charlie Chicago David San Eve Boston Frank Seattle Grace Phoenix
[ ,:]
:这是一个字符集,表示匹配方括号里面任意一个字符;+
:这是一个量词,表示前面的字符集[ ,:]
可以出现一次或多次。换句话说,它会匹配一个或多个连续的空格、逗号或冒号。字段变量的动态访问
除了直接使用
$1
、$2
等字段变量,还可以通过变量动态访问字段。例如,提取最后一个字段:shellawk '{print $NF}' data.txt
$NF
表示当前行的最后一个字段。NF 是一个内置变量,表示当前行的字段数。字段分割和重新生成
awk 允许直接修改字段变量的值,并且会自动更新整行内容(
$0
),例如:shell[root@localhost ~]# awk -F'[ :,]+' '{$2 = $2 + 1; print}' data.txt Alice 31 New York Bob 26 Los Angeles Charlie 36 Chicago David 41 San Francisco Eve 23 Boston Frank 34 Seattle Grace 29 Phoenix
在这个例子中,修改了
$2
的值,将第 2 个字段的值加 1。由于$2
被修改了,awk 会自动重新生成$0
,即整行内容。print
会默认打印当前行的内容(即$0
)。除此之外,可以使用内置函数,例如修改文本大小写:
shell[root@localhost ~]# awk -F'[ ,:]' '{ $3 = toupper($3); print }' data.txt Alice 30 NEW York Bob 25 LOS Angeles Charlie 35 CHICAGO David 40 SAN Francisco Eve 22 BOSTON Frank 33 SEATTLE Grace 28 PHOENIX
将第 3 列改为大写。
提取字段并用竖号分隔输出
shell[root@localhost ~]# awk -F'[ ,:]' -v OFS='|' '{print $1, $2, $3, $4}' data.txt Alice|30|New|York Bob|25|Los|Angeles Charlie|35|Chicago| David|40|San|Francisco Eve|22|Boston| Frank|33|Seattle| Grace|28|Phoenix|
-v
选项用于在命令行中为awk
脚本设置变量的值。它的作用是将外部的值传递给awk
脚本中的变量。这样可以在脚本运行之前初始化变量,而不需要在脚本内部赋值。常用于设置输入分隔符(-F
)、输出分隔符(OFS
)、条件值等。OFS='|'
表示在输出时,用竖线(|
)分隔字段。条件匹配
只打印第 2 列大于 25 的行:
shell[root@localhost ~]# awk -F'[ ,:]' '$2 > 25 {print}' data.txt Alice 30 New York Charlie 35 Chicago David:40:San Francisco Frank 33 Seattle Grace,28,Phoenix
BEGIN
BEGIN
是 awk
中的一个特殊模式,用于在处理任何输入之前执行特定的动作。它通常用于初始化变量、设置格式或执行一些预处理操作。BEGIN
块在 awk
程序的开始处执行,且仅执行一次。
BEGIN { action }
BEGIN
:特殊模式,表示在处理任何输入之前执行。action
:在 BEGIN 块中要执行的动作,通常用{}
包裹。
BEGIN
的用途:
- 初始化变量:在处理输入之前设置初始值。
- 设置格式:定义输出格式或字段分隔符。
- 预处理操作:执行一些在处理输入之前需要完成的任务。
BEGIN
是 awk
的关键字,它必须大写。它会强制 awk
在读取数据前执行该关键字后指定的脚本命令。例如,可以在 BEGIN
块中初始化一些变量或输出头信息:
awk 'BEGIN {
# 设置字段分隔符为冒号
FS = ":";
# 打印表头
print "用户名、t 主目录、t 登录 Shell";
}
{
# 打印每一行的用户名、主目录和登录 Shell
print $1, "\t", $6, "\t", $7;
}' /etc/passwd
用户名 主目录 登录 Shell
root /root /bin/shell
bin /bin /sbin/nologin
daemon /sbin /sbin/nologin
…………
在这个例子中,BEGIN
用于设置字段分隔符 FS
为冒号 :
,并打印出标题行。其中 \t
为制表符(tab),以便在输出中对齐各列数据。
还可以在 BEGIN
模式中定义数组,用于存储数据或统计信息。例如,定义一个数组存储一些预定义的值:
awk 'BEGIN {
array["Alice"] = 25;
array["Bob"] = 30;
print array["Alice"];
}'
此命令将 Alice
映射到 25
,Bob
映射到 30
,并打印 array["Alice"]
的值,输出为 25。
END
END
是 awk
中的一个特殊模式,用于在处理完所有输入后执行特定的动作。它通常用于总结、打印统计结果或执行清理操作。END
块在 awk
程序的最后执行,且仅执行一次。
END { action }
END
:特殊模式,表示在处理完所有输入后执行。action
:在END
块中要执行的动作,通常用{}
包裹。
END
的用途:
- 总结和打印统计结果:在处理完所有输入后,输出一些汇总信息。
- 执行清理操作:例如关闭文件、释放资源等。
- 检查条件:在处理完所有数据后,检查某些条件是否满足。
awk '
{
print;
}
END {
print "行数:", NR;
}' /etc/passwd
root:x:0:0:root:/root:/bin/shell
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
……
行数:20
可以看到,当 awk
程序先执行 print
打印完文件内容后,才会执行 END 中的脚本命令。如果没有 print
则不会打印文件内容。
变量
在 awk
的脚本程序中,支持使用变量来存取值。awk
支持两种不同类型的变量:
- 内建变量:
awk
本身就创建好,用户可以直接拿来用的变量,这些变量用来存放处理数据文件中的某些字段和记录的信息。 - 自定义变量:
awk
支持用户自己创建变量。
内置变量
awk
程序使用内建变量来引用程序数据里的一些特殊功能。常见的一些内建变量:
常见变量 | 功能 |
---|---|
FIELDWIDTHS | 用于指定固定宽度字段的宽度。 |
NR | 表示输入的当前记录编号。(即行号)。 |
FNR | 表示当前输入文件中的记录编号(即行号)。它与 NR 变量类似,但 NR 表示所有输入文件中的记录编号,而 FNR 仅表示当前文件中的记录编号。 |
NF | 表示当前记录中的字段数。 |
FS | 表示输入字段分隔符,默认为空格。 |
RS | 表示输入记录分隔符,默认为换行符(\n )。 |
OFS | 表示输出字段分隔符,默认为空格。 |
ORS | 表示输出记录分隔符,默认为换行符(\n )。 |
其他变量:
其他变量 | 功能 |
---|---|
ARGC | 命令行参数个数。 |
ARGIND | 当前文件在 ARGC 中的位置。 |
ARGV | 包含命令行参数的数组。 |
CONVFMT | 数字的转换格式,默认值为 %.6g。 |
ENVIRON | 当前 shell 环境变量及其值组成的关联数组。 |
ERRNO | 当读取或关闭输入文件发生错误时的系统错误号。 |
FILENAME | 当前输入文档的名称。 |
IGNORECASE | 设成非 0 值时,忽略 awk 命令中出现的字符串的字符大小写。 |
OFMT | 数字的输出格式,默认值为 %.6g。 |
RLENGTH | 由 match 函数所匹配的子字符串的长度。 |
RSTART | 由 match 函数所匹配的子字符串的起始位置。 |
FS 和 OFS
变量 FS
和 OFS
定义了 awk
如何处理数据流中的数据字段。变量 FS
来定义记录中的字段分隔符,变量 OFS
具备相同的功能,只不过是用在输出 print
命令的输出上,例如:
awk 'BEGIN {
FS = ":";
OFS = ",";
}
{
print $1, $2, $3;
}' /etc/passwd
root,x,0
bin,x,1
daemon,x,2
这个例子中的 awk
命令用于处理 /etc/passwd
文件。它首先在 BEGIN 块中设置了 FS
和 OFS
的值。
将 FS
的值设置为冒号 :
,意味着 awk 会将输入数据中的冒号作为字段分隔符。将 OFS
的值设置为逗号 ,
,意味着 awk
会在输出数据中使用逗号作为字段分隔符。
然后,对于 /etc/passwd
文件中的每一行数据,awk
都会执行 { print $1, $2, $3 }
中的操作,打印出该行的前三个字段。由于设置了 OFS
的值,所以在输出数据中,各个字段之间会用逗号 ,
分隔。
FIELDWIDTHS
FIELDWIDTHS
变量允许用户不依靠字段分隔符来读取记录。在一些应用程序中,数据并没有使用字段分隔符,而是被放置在了记录中的特定列,这种情况下,必须设定 FIELDWIDTHS
变量来匹配数据在记录中的位置。
一旦设置了 FIELDWIDTH
变量,awk
就会忽略 FS
变量,并根据提供的字段宽度来计算字段,下面是个采用字段宽度而非字段分隔符的例子:
13111111111
15011111111
17211111111
18311111111
awk 'BEGIN {
FIELDWIDTHS = "3 4 4";
}
{
print $1, $2, $3;
}' iphone.txt
131 1111 1111
150 1111 1111
172 1111 1111
183 1111 1111
注意,FIELDWIDTHS
适用于固定宽度的字段,每个字段的宽度是固定的,如果字段宽度不固定,就无法正确分割字段。
RS 和 ORS
变量 RS
和 ORS
定义了 awk
程序如何处理数据流中的字段,默认情况下,awk
将 RS
和 ORS
设为换行符。默认的 RS
值表明,输入数据流中的每行新文本就是一条新纪录。有时,会在数据流中碰到占据多行的字段。
典型的例子是包含地址和电话号码的数据,其中地址和电话号码各占一行,例如:
4 Jersey St, Boston, MA 02215
Fenway Park
+18777337699
Anaheim, CA 92802
Disneyland Park
+17147814636
1525 Bernice St, Honolulu, HI 96817 美国
Bishop Museum
+18088473511
awk 'BEGIN {
FS = "\n";
RS = "";
ORS = "\n\n";
}
{
print $1, $2, $3;
}' address.txt
4 Jersey St, Boston, MA 02215 Fenway Park +18777337699
Anaheim, CA 92802 Disneyland Park +17147814636
1525 Bernice St, Honolulu, HI 96817 美国 Bishop Museum +18088473511
BEGIN{FS="\n";RS="";ORS="\n\n"}
:
FS="\n"
:设置字段分隔符为换行符\n
,每行是一个字段。RS=""
:设置记录分隔符为空字符串,记录由空行分隔。ORS="\n\n"
:设置输出记录分隔符为两个换行符,每个输出记录之间会有一个额外的空行。
通过设置 ORS
,可以控制输出记录之间的分隔符,从而调整输出的格式。这在需要在输出中添加额外的空行或特殊分隔符时非常有用。
FNR 和 NR
FNR
和 NR
变量虽然类似,但又略有不同。FNR
和 NR
都表示当前记录的编号,但它们的区别在于
NR
:表示从 awk 开始执行程序后所读取的数据行数,是一个全局变量,对所有输入文件的行进行累计计数。FNR
:表示当前文件中已经读入的记录数,是一个局部变量,每读取一个新文件时,FNR
会重新从 1 开始计数。
假设有两个文件 file1 和 file2,它们的内容分别为:
Alice 30 New York
Bob 25 Los Angeles
Charlie 35 Chicago
David 40 San Francisco
现在运行下面这个 awk
命令,这个命令会输出每一行的 FNR
值、NR
值和内容:
awk '
{
print FNR, NR, $0;
}' nr.txt fnr.txt
1 1 Alice 30 New York
2 2 Bob 25 Los Angeles
1 3 Charlie 35 Chicago
2 4 David 40 San Francisco
可以看到,对于每个文件,FNR
都是从 1 开始计数的,而 NR
则是从第一个文件开始一直累加到最后一个文件。这就是 FNR
和 NR
的区别。
自定义变量
和其他典型的编程语言一样,awk
允许用户定义自己的变量在脚本程序中使用。awk
自定义变量名可以是任意数目的字母、数字和下划线,但不能以数字开头。更重要的是,awk
变量名区分大小写。
定义自定义变量非常简单,只需要在使用变量之前给它赋一个初始值即可。例如:
awk 'BEGIN {
var_test = "10";
print var_test;
}'
10
上面这个例子中,定义了一个名为 var_test
的自定义变量,并给它赋了初始值 10。然后使用 print
语句输出了这个变量的值。
在 awk
中,变量的类型是动态的,也就是说,变量的类型会根据它所存储的值的类型而改变。例如,如果给一个变量赋了一个字符串值,那么这个变量就是字符串类型的;如果给一个变量赋了一个数字值,那么这个变量就是数字类型的。例如:
awk 'BEGIN {
var_test = "10";
print var_test;
var_test = "Hello, awk!";
print var_test;
}'
10
Hello,awk!
awk
还可以给程序中的变量赋值,这允许在正常的代码之外赋值,即时改变变量的值,例如:
111 222 333
444 555 666
777 888 999
awk '{print $n}' n=2 var.txt
222
555
888
在这个例子中,awk
程序会打印出文件 var.txt 中每一行的第 n
个字段,也就是第二个字段。
需要注意的是,这种方法只能在 awk
程序的主体部分中使用,不能在 BEGIN
块中使用。因为在 BEGIN
块中,awk
还未开始处理输入文件,因此 $n
无法引用具体的字段。
如果需要在 BEGIN
块中使用变量,可以使用 -v
选项在脚本运行之前为变量赋值:
awk -v n=2 'BEGIN {
print "Column:", n;
}
{
print $n;
}' var.txt
Column: 2
222
555
888
在这个例子中,在脚本运行之前,使用 -v n=2
将变量 n
初始化为 2。然后,在 awk
程序的 BEGIN
块中,打印出变量 n
的值。在 awk
程序的主体部分,打印出文件 var.txt 中每一行的第 n
个字段,也就是第二个字段。
关联数组
在 awk
中,数组是关联数组(Associative Arrays),这意味着数组的索引可以是数字或字符串,而不是像传统数组那样仅限于连续的数字索引。
数组
数组是一种数据结构,用于存储一系列的值。这些值可以是数字、字符串或其他类型的数据。数组中的每个值都有一个唯一的索引(或键),用于访问该值。
awk
关联数组的特点:
- 索引可以是数字或字符串:在
awk
中,数组的索引不仅可以是数字,还可以是任意字符串。这种灵活性使得 awk 的数组非常强大。 - 无需提前声明大小:
awk
的数组不需要提前声明大小,它会在运行时根据需要自动扩展或收缩。 - 索引的唯一性:每个索引字符串必须唯一地标识出赋给它的数据元素。如果尝试为同一个索引赋值多次,后面的值会覆盖前面的值。
- 遍历顺序不固定:关联数组的遍历顺序是随机的,不保证与插入顺序一致。
数组定义和使用
在 awk
脚本程序中,定义一个数组变量可以使用标准复制语句,其基本格式为:
array_name[index]=value
array_name
:数组名称。index
:数组索引。value
是数组中元素所赋予的值。
例如:
fruits["芒果"] = "橘色";
fruits["橘子"] = "黄色";
fruits["苹果"] = "红色";
这里,fruits
是数组名,芒果
和 橘子
是索引(键),橘色
和 黄色
是对应的值。
注意,在引用数组变量时,必须用索引值(index)来提取相应的数据元素值,例如:
awk 'BEGIN {
fruits["芒果"] = "橘色";
fruits["橘子"] = "黄色";
print fruits["芒果"];
print fruits["橘子"];
}'
橘色
黄色
数组变量也是变量,也可以使用其进行基本的算术运算,例如:
awk 'BEGIN {
num[1] = 10;
num[2] = 20;
sum = num[1] + num[2];
print sum;
}'
30
关联数组的遍历
在 awk
中遍历关联数组,可以用 for
语句的一种特殊形式:
for (variable in array) {
statements
}
variable
是循环变量,它会在每次迭代时被赋值为数组的一个索引。array
是要遍历的关联数组。
在循环体中,可以使用 array[variable]
来访问当前索引对应的元素。注意的是,整个遍历过程中,传给 variable
的都是每个数组元素的索引值(也就是 index
),不是数组元素的值。
例如:
awk 'BEGIN {
fruits["芒果"] = "橘色";
fruits["橘子"] = "黄色";
fruits["苹果"] = "红色";
for (key in fruits) {
print "水果:", key,"," "颜色:", fruits[key];
}
}'
水果:苹果 ,颜色:红色
水果:芒果 ,颜色:橘色
水果:橘子 ,颜色:黄色
需要注意的是,关联数组中元素的顺序是不确定的。因此,遍历关联数组时,元素的顺序也是不确定的。
删除数组变量
awk
脚本程序还支持从关联数组中删除某个数组索引,使用 delete
命令就可以,此命令会从数组中删除指定的索引值及相关的数据元素的值。其基本格式如下:
delete array[index]
array
是要删除元素的数组。index
是要删除的元素的索引。
例如:
awk 'BEGIN {
fruits["芒果"] = "橘色";
fruits["橘子"] = "黄色";
fruits["苹果"] = "红色";
delete fruits["苹果"];
for (key in fruits) {
print "水果:", key,"," "颜色:", fruits[key];
}
}'
水果:芒果 ,颜色:橘色
水果:橘子 ,颜色:黄色
除此之外,还可以使用 delete
语句来删除整个数组,例如:
awk 'BEGIN {
fruits["芒果"] = "橘色";
fruits["橘子"] = "黄色";
fruits["苹果"] = "红色";
delete fruits;
for (key in fruits) {
print "水果:", key,"," "颜色:", fruits[key];
}
}'
这段代码不会输出任何内容,因为整个数组都被删除了。
使用分支结构
awk
支持标准的 if-then-else
格式的 if
语句,其基本格式为:
if (condition)
statement1
else
statements2
也可以将它放在一行上,像这样:
if (condition) statement1;else statement2
例如,根据日志关键词标记错误级别(ERROR/WARNING/INFO):
[2023-10-01 08:00] INFO Server started
[2023-10-01 08:05] WARNING High memory usage
[2023-10-01 08:10] ERROR Database connection failed
[2023-10-01 08:15] INFO User login
awk '{
if ($3 == "ERROR") {
color = "\033[31m" # 红色
} else if ($3 == "WARNING") {
color = "\033[33m" # 黄色
} else {
color = "\033[32m" # 绿色
}
printf "%s[%-8s]\033[0m %s\n", color, $3, $0
}' app.log
这段 awk 代码的主要功能是根据日志文件 app.log 中每行的第三个字段(即日志级别),使用不同的颜色(红色、黄色或绿色)来标记该日志级别,并将整行日志信息输出。
[INFO ] [2023-10-01 08:00] INFO Server started
[WARNING ] [2023-10-01 08:05] WARNING High memory usage
[ERROR ] [2023-10-01 08:10] ERROR Database connection failed
[INFO ] [2023-10-01 08:15] INFO User login
printf
printf 是一种非常强大的输出格式化操作符,常用于在程序中控制输出的格式。它允许以高度可读和标准化的方式格式化文本和数据。
使用循环结构
awk
提供了三种主要的循环结构:while
、do-while
和 for
。这些循环结构允许重复执行特定的操作,非常适合处理文本文件中的多行数据或执行重复性任务。
while
while
循环的语法如下:
while (condition) {
statements
}
condition
是一个布尔表达式。在每次迭代开始时,都会检查 condition
的值。如果 condition
的值为真,那么 while
循环后面的语句块中的语句就会被执行;否则,循环就会结束。
特点:
- 在每次迭代开始时检查条件。
- 如果条件为真,执行循环体。
- 适合在条件不满足时结束循环。
例如,统计数字 1 到 10 的平方和
awk 'BEGIN {
sum = 0;
i = 1;
while (i <= 10) {
sum += i * i;
i++;
}
print "1 到 10 的平方和为:", sum;
}'
1 到 10 的平方和为: 385
do-while
do-while
循环确保循环体至少执行一次,然后在每次迭代结束时检查条件。它的语法如下:
do {
statements
} while (condition)
condition
是一个布尔表达式。与 while
循环不同的是,在每次迭代结束时才会检查 condition
的值。这意味着,无论 condition
的初始值是什么,do-while
循环都至少会执行一次。
特点:
- 循环体至少执行一次。
- 适合在条件不满足时结束循环。
例如,下面的代码会使用 do-while 循环来打印数字 1 到 5:
awk 'BEGIN {
i = 1;
do {
print i;
i++;
} while (i <= 5)
}'
1
2
3
4
5
for
for
循环是常用的循环结构,适合在已知迭代次数的情况下使用。它的语法如下:
for (initialization; condition; increment) {
statements
}
其中,initialization
是一个表达式,它会在循环开始之前执行一次。condition
是一个布尔表达式,在每次迭代开始时都会检查它的值。如果 condition
的值为真,那么 for
循环后面的语句块中的语句就会被执行;否则,循环就会结束。在每次迭代结束时,都会执行一次 increment
表达式。
特点:
- 初始化表达式在循环开始前执行一次。
- 条件表达式在每次迭代开始时检查。
- 增量表达式在每次迭代结束时执行。
例如,打印九九乘法表:
awk 'BEGIN {
print "九九乘法表:";
for (i = 1; i <= 9; i++) {
for (j = 1; j <= i; j++) {
printf "%d×%d=%-3d", j, i, i*j;
}
print "";
}
}'
九九乘法表:
1×1=1
1×2=2 2×2=4
1×3=3 2×3=6 3×3=9
1×4=4 2×4=8 3×4=12 4×4=16
1×5=5 2×5=10 3×5=15 4×5=20 5×5=25
1×6=6 2×6=12 3×6=18 4×6=24 5×6=30 6×6=36
1×7=7 2×7=14 3×7=21 4×7=28 5×7=35 6×7=42 7×7=49
1×8=8 2×8=16 3×8=24 4×8=32 5×8=40 6×8=48 7×8=56 8×8=64
1×9=9 2×9=18 3×9=27 4×9=36 5×9=45 6×9=54 7×9=63 8×9=72 9×9=81
break 和 continue
除此之外,awk 还支持使用 break
、continue
关键字:
break
:用于立即跳出当前循环。continue
:用于跳过当前循环的剩余部分,直接开始下一次循环。
使用
break
跳出循环break
用于跳出当前循环,终止循环的执行:awkawk 'BEGIN { for (i = 1; i <= 9; i++) { if (i == 5) { break; # 在遇到数字 5 时跳出循环 } print i; } }'
text1 2 3 4 6 7 8 9
使用 for 循环遍历数字 1 到 9,如果当前数字等于 5,使用
break
终止循环,也就是说其他数字正常打印,但在遇到数字 5 后停止。使用
continue
跳过当前循环continue
跳过当前循环的剩余部分,直接进入下一次循环:awkawk 'BEGIN { for (i = 1; i <= 9; i++) { if (i == 5) { continue; # 跳过数字 5 } print i; } }'
text1 2 3 4 6 7 8 9
使用
for
循环遍历数字 1 到 9,如果当前数字等于 5,使用continue
跳过当前循环,直接进入下一次循环。其他数字正常打印。
函数
在 awk
中,函数是一种可以重复使用的代码块,它可以接受一些输入参数,执行特定的操作,并返回一个结果。函数是 awk
脚本的核心组件之一,用于封装和复用代码,使脚本更加模块化和易于维护。
内置函数
和内建变量类似,awk
提供了许多内置函数,用于执行常见的操作,如字符串处理、数学计算和时间日期处理。
字符串函数,用于处理和转换字符串:
字符串函数 | 介绍 | 示例 |
---|---|---|
toupper(string) | 将字符串中的所有小写字母转换为大写字母。 | awk '{print toupper($1)}' |
tolower(string) | 将字符串中的所有大写字母转换为小写字母。 | awk '{print tolower($1)}' |
length(string) | 返回字符串的长度。如果未提供参数,则返回 $0 的长度。 | awk '{print length($1)}' |
substr(string, start, length) | 返回字符串的子字符串,从 start 开始,长度为 length 。 | awk '{print substr($1, 1, 3)}' |
index(string, target) | 返回子字符串 target 在字符串 string 中的位置,未找到时返回 0。 | awk '{print index($1, "li")}' |
match(string, regex) | 返回正则表达式 regex 在字符串 string 中匹配的位置,未找到时返回 0。 | awk '{print match($1, /li/)}' |
split(string, array, separator) | 将字符串按分隔符分割,并将结果存储到数组中。返回分割后的字段数。 | awk '{split($1, arr, ""); print arr[1], arr[2]}' |
数值函数,用于数学运算:
数值函数 | 介绍 | 示例 |
---|---|---|
int(x) | 返回数值的整数部分。 | awk '{print int($2)}' |
sqrt(x) | 返回数值的平方根。 | awk '{print sqrt($2)}' |
sin(x) | 返回数值的正弦值。 | awk '{print sin($2)}' |
cos(x) | 返回数值的余弦值。 | awk '{print cos($2)}' |
atan2(y, x) | 返回 y/x 的反正切值。 | awk '{print atan2($2, $3)}' |
exp(x) | 返回 e 的 x 次幂。 | awk '{print exp($2)}' |
log(x) | 返回数值的自然对数。 | awk '{print log($2)}' |
输入输出函数,用于控制数据的读取和格式化输出:
输入输出函数 | 介绍 | 示例 |
---|---|---|
printf(format, ...) | 格式化输出,类似于 C 语言中的 printf 。 | awk '{printf "Name: %-10s Age: %d\n", $1, $2}' |
sprintf(format, ...) | 将格式化的字符串存储到变量中。 | awk '{msg = sprintf("Name: %-10s Age: %d", $1, $2); print msg}' |
close(filename) | 关闭文件或管道。 | awk '{print $1 > "output.txt"} END {close("output.txt")}' |
getline | 从文件或管道中读取下一行。 | awk '{getline nextline < "data.txt"; print nextline}' |
其他函数,如随机数生成、系统命令执行等:
其他函数 | 介绍 | 示例 |
---|---|---|
srand(seed) | 设置随机数种子。 | awk 'BEGIN {srand(123); print rand()}' |
rand() | 返回 0 到 1 之间的随机数。 | awk 'BEGIN {print rand()}' |
system(command) | 执行外部命令。 | awk 'BEGIN {system("date")}' |
自定义函数
在 awk
中,可以定义自己的函数,这些函数可以接受参数并返回值。定义函数的基本格式如下:
function function_name(argument1, argument2, ...) {
# 函数体
function body
}
function_name
:函数的名称,以字母开头,可以包含字母、数字或下划线。不能使用 awk 的保留关键字作为函数名。argument1, argument2, ...
:函数的参数,通过逗号分隔。参数是可选的,也可以定义没有参数的函数。function body
:函数体,包含awk
程序代码。
定义一个简单的函数
用于打印记录中的第三个字段:
awkfunction print_third() { print $3 }
定义一个返回值的函数
awkfunction myrand(limit) { return int(limit * rand()) }
用于返回一个随机整数。
awk
允许将多个函数存储在一个库文件中,这样可以在多个 awk
脚本中复用这些函数,创建一个文件 functions.awk,存储所有函数:
# 返回两个数字的和
function add(num1, num2) {
return num1 + num2
}
# 返回两个数字的差
function subtract(num1, num2) {
return num1 - num2
}
# 返回两个数字的乘积
function multiply(num1, num2) {
return num1 * num2
}
# 返回两个数字的商
function divide(num1, num2) {
if (num2 != 0) {
return num1 / num2
} else {
return "Error: Division by zero"
}
}
# 主函数
function main(num1, num2) {
# 计算和
result_add = add(num1, num2)
print "Sum =", result_add
# 计算差
result_subtract = subtract(num1, num2)
print "Difference =", result_subtract
# 计算乘积
result_multiply = multiply(num1, num2)
print "Product =", result_multiply
# 计算商
result_divide = divide(num1, num2)
print "Quotient =", result_divide
}
创建一个脚本文件 script.awk,调用库文件中的函数:
BEGIN {
main(10, 5)
}
运行脚本时,需要使用 -f
选项加载库文件和脚本文件:
[root@localhost ~]# awk -f functions.awk -f script.awk
Sum = 15
Difference = 5
Product = 50
Quotient = 2