高级 gawk 编程指南
1. 变量相关操作
1.1 用户自定义变量
gawk 允许用户自定义变量,变量名可以由字母、数字和下划线组成,但不能以数字开头,并且变量名区分大小写。
1.1.1 在脚本中赋值变量
在 gawk 程序中给变量赋值与在 shell 脚本中类似,使用赋值语句。例如:
$ gawk '
> BEGIN{
> testing="This is a test"
> print testing
> }'
This is a test
$
gawk 变量可以保存数值或文本值,还可以通过赋值语句改变变量的值,如:
$ gawk '
> BEGIN{
> testing="This is a test"
> print testing
> testing=45
> print testing
> }'
This is a test
45
$
赋值语句还可以包含数学算法来处理数值,gawk 包含标准的数学运算符,如:
$ gawk 'BEGIN{x=4; x= x * 2 + 3; print x}'
11
$
1.1.2 在命令行赋值变量
可以使用 gawk 命令行给变量赋值,从而在正常代码之外设置值,实现动态更改。例如,使用命令行变量显示文件中的特定数据字段:
$ cat script1
BEGIN{FS=","}
{print $n}
$ gawk -f script1 n=2 data1
data12
data22
data32
$ gawk -f script1 n=3 data1
data13
data23
data33
$
不过,使用命令行参数定义变量值时,在代码的 BEGIN 部分无法获取该值。可以使用 -v 命令行参数解决此问题,-v 参数必须放在命令行中的脚本代码之前。例如:
$ cat script2
BEGIN{print "The starting value is",n; FS=","}
{print $n}
$ gawk -f script2 n=3 data1
The starting value is
data13
data23
data33
$ gawk -v n=3 -f script2 data1
The starting value is 3
data13
data23
data33
$
1.2 数组操作
gawk 使用关联数组来存储多个值。关联数组的索引值可以是任何文本字符串,与数值数组不同,不需要使用顺序数字来标识数组中的数据元素。
1.2.1 定义数组变量
可以使用标准赋值语句定义数组变量,格式为
var[index] = element
,其中
var
是变量名,
index
是关联数组的索引值,
element
是数据元素值。例如:
capital["Illinois"] = "Springfield"
capital["Indiana"] = "Indianapolis"
capital["Ohio"] = "Columbus"
引用数组变量时,必须包含索引值以获取相应的数据元素值:
$ gawk 'BEGIN{
> capital["Illinois"] = "Springfield"
> print capital["Illinois"]
> }'
Springfield
$
数组变量也可以存储数值数据元素值,并且可以像其他变量一样使用:
$ gawk 'BEGIN{
> var[1] = 34
> var[2] = 3
> total = var[1] + var[2]
> print total
> }'
37
$
1.2.2 遍历数组变量
关联数组的索引值可能是任意的,因此可能不知道具体的索引值。可以使用特殊格式的 for 语句遍历关联数组:
for (var in array)
{
statements
}
例如:
$ gawk 'BEGIN{
> var["a"] = 1
> var["g"] = 2
> var["m"] = 3
> var["u"] = 4
> for (test in var)
> {
> print "Index:",test," - Value:",var[test]
> }
> }'
Index: u - Value: 4
Index: m - Value: 3
Index: a - Value: 1
Index: g - Value: 2
$
注意,索引值不会按特定顺序返回,但每个索引都对应正确的数据元素值。
1.2.3 删除数组变量
从关联数组中删除数组索引需要使用特殊命令
delete array[index]
,该命令会删除关联索引值和关联的数据元素值:
$ gawk 'BEGIN{
> var["a"] = 1
> var["g"] = 2
> for (test in var)
> {
> print "Index:",test," - Value:",var[test]
> }
> delete var["g"]
> print "---"
> for (test in var)
> print "Index:",test," - Value:",var[test]
> }'
Index: a - Value: 1
Index: g - Value: 2
---
Index: a - Value: 1
$
一旦从关联数组中删除索引值,就无法再恢复。
2. 匹配模式
gawk 程序支持多种匹配模式来过滤数据记录,类似于 sed 编辑器。
2.1 正则表达式
可以使用基本正则表达式(BRE)或扩展正则表达式(ERE)来过滤数据流中的行。正则表达式必须出现在它控制的程序脚本左花括号之前。例如:
$ gawk 'BEGIN{FS=","} /11/{print $1}' data1
data11
$
正则表达式
/11/
匹配数据字段中任意位置包含字符串 11 的记录。gawk 会将定义的正则表达式与记录中的所有数据字段进行匹配,包括字段分隔符字符:
$ gawk 'BEGIN{FS=","} /,d/{print $1}' data1
data11
data21
data31
$
但这种方式可能会导致匹配特定数据字段时出现问题,此时应使用匹配运算符。
2.2 匹配运算符
匹配运算符
~
允许将正则表达式限制在记录的特定数据字段上。例如:
$1 ~ /^data/
该表达式过滤第一条数据字段以文本
data
开头的记录。示例代码如下:
$ gawk 'BEGIN{FS=","} $2 ~ /^data2/{print $0}' data1
data21,data22,data23,data24,data25
$
还可以使用
!
符号否定正则表达式匹配,例如:
$ gawk –F: '$1 !~ /rich/{print $1,$NF}' /etc/passwd
root /bin/bash
daemon /bin/nologin
bin /bin/nologin
sys /bin/nologin
--- output truncated ---
$
2.3 数学表达式
除了正则表达式,还可以在匹配模式中使用数学表达式,这在匹配数据字段中的数值时很有用。例如,显示属于根用户组(组号为 0)的所有系统用户:
$ gawk -F: '$4 == 0{print $1}' /etc/passwd
root
$
可以使用的数学比较表达式如下:
-
x == y
:值 x 等于 y。
-
x <= y
:值 x 小于或等于 y。
-
x < y
:值 x 小于 y。
-
x >= y
:值 x 大于或等于 y。
-
x > y
:值 x 大于 y。
使用文本数据时,表达式是精确匹配,数据必须与模式完全匹配。例如:
$ gawk -F, '$1 == "data"{print $1}' data1
$
$ gawk -F, '$1 == "data11"{print $1}' data1
data11
$
3. 结构化命令
gawk 编程语言支持常见的结构化编程命令。
3.1 if 语句
gawk 支持标准的 if-then-else 格式的 if 语句。需要定义一个条件,用括号括起来,如果条件为真,则执行 if 语句后面的语句;如果条件为假,则跳过该语句。可以使用以下格式:
if (condition)
statement1
或者放在一行:
if (condition) statement1
例如:
$ cat data4
10
5
13
50
34
$ gawk '{if ($1 > 20) print $1}' data4
50
34
$
如果需要在 if 语句中执行多条语句,必须用花括号将它们括起来:
$ gawk '{
> if ($1 > 20)
> {
> x = $1 * 2
> print x
> }
> }' data4
100
68
$
要注意不要混淆 if 语句的花括号和程序脚本的花括号,gawk 程序可以检测到缺少的花括号并给出错误信息。
gawk 的 if 语句还支持 else 子句,当 if 语句条件不满足时执行 else 子句中的语句。例如:
$ gawk '{
> if ($1 > 20)
> {
> x = $1 * 2
> print x
> } else
> {
> x = $1 / 2
> print x
> }}' data4
5
2.5
6.5
100
68
$
也可以将 else 子句放在一行,但需要在 if 语句部分后面使用分号:
if (condition) statement1; else statement2
示例代码如下:
$ gawk '{if ($1 > 20) print $1 * 2; else print $1 / 2}' data4
5
2.5
6.5
100
68
$
3.2 while 语句
while 语句为 gawk 程序提供基本的循环功能,格式如下:
while (condition)
{
statements
}
例如:
$ cat data5
130 120 135
160 113 140
145 170 215
$ gawk '{
> total = 0
> i = 1
> while (i < 4)
> {
> total += $i
> i++
> }
> avg = total / 3
> print "Average:",avg
> }' data5
Average: 128.333
Average: 137.667
Average: 176.667
$
gawk 编程语言支持在 while 循环中使用 break 和 continue 语句,允许跳出循环中间部分。例如:
$ gawk '{
> total = 0
> i = 1
> while (i < 4)
> {
> total += $i
> if (i == 2)
> break
> i++
> }
> avg = total / 2
> print "The average of the first two data elements is:",avg
> }' data5
The average of the first two data elements is: 125
The average of the first two data elements is: 136.5
The average of the first two data elements is: 157.5
$
3.3 do-while 语句
do-while 语句与 while 语句类似,但在检查条件语句之前执行语句。格式如下:
do
{
statements
} while (condition)
例如:
$ gawk '{
> total = 0
> i = 1
> do
> {
> total += $i
> i++
> } while (total < 150)
> print total }' data5
250
160
315
$
这种格式保证在评估条件之前至少执行一次语句。
3.4 for 语句
gawk 编程语言支持 C 风格的 for 循环:
for( variable assignment; condition; iteration process)
例如:
$ gawk '{
> total = 0
> for (i = 1; i < 4; i++)
> {
> total += $i
> }
> avg = total / 3
> print "Average:",avg
> }' data5
Average: 128.333
Average: 137.667
Average: 176.667
$
通过在 for 循环中定义迭代计数器,无需像使用 while 语句那样手动递增计数器。
4. 格式化打印
print 语句对 gawk 显示数据的控制有限,若需要创建详细报告,通常需要将数据放在特定格式和位置,此时可以使用格式化打印命令 printf。
4.1 printf 命令格式
printf 命令的格式为:
printf "format string", var1, var2
格式字符串是格式化输出的关键,它使用文本元素和格式说明符指定格式化输出的外观。格式说明符是一个特殊代码,指示要显示的变量类型和显示方式。gawk 程序使用每个格式说明符作为命令中列出的每个变量的占位符。
4.2 格式说明符
格式说明符的格式为
%[modifier]control-letter
,其中控制字母指示要显示的数据值类型,修饰符定义可选的格式化功能。控制字母如下表所示:
| 控制字母 | 描述 |
| ---- | ---- |
| c | 将数字显示为 ASCII 字符 |
| d | 显示整数值 |
| i | 显示整数值(与 d 相同) |
| e | 以科学记数法显示数字 |
| f | 显示浮点值 |
| g | 显示科学记数法或浮点数,取较短者 |
| o | 显示八进制值 |
| s | 显示文本字符串 |
| x | 显示十六进制值 |
| X | 显示十六进制值,但 A 到 F 使用大写字母 |
例如,使用
%e
格式说明符显示大数值:
$ gawk 'BEGIN{
> x = 10 * 100
> printf "The answer is: %e\n", x
> }'
The answer is: 1.000000e+03
$
4.3 修饰符
除了控制字母,还有三个修饰符可用于更精确地控制输出:
-
width
:一个数值,指定输出字段的最小宽度。如果输出较短,printf 会用空格填充输出,文本使用右对齐;如果输出比指定宽度长,则会覆盖宽度值。
-
prec
:一个数值,指定浮点数小数点右侧的位数,或文本字符串中显示的最大字符数。
-
-
(减号):减号表示在格式化空间中放置数据时使用左对齐而不是右对齐。
例如,将 print 命令转换为 printf 命令:
$ gawk 'BEGIN{FS="\n"; RS=""} {print $1,$4}' data2
Ima Test (312)555-1234
Frank Tester (317)555-9876
Haley Example (313)555-4938
$
$ gawk 'BEGIN{FS="\n"; RS=""} {printf "%s %s\n", $1, $4}' data2
Ima Test (312)555-1234
Frank Tester (317)555-9876
Haley (313)555-4938
$
注意,需要手动在 printf 命令末尾添加换行符以强制换行。如果需要在同一行打印多个内容,可以使用多个 printf 命令,并在 END 部分打印一个换行符来结束该行:
$ gawk 'BEGIN{FS=","} {printf "%s ", $1} END{printf "\n"}' data1
data11 data21 data31
$
下面是一个简单的 mermaid 流程图,展示使用 printf 打印数据的流程:
graph TD;
A[开始] --> B[定义格式字符串和变量];
B --> C[执行 printf 命令];
C --> D[输出格式化数据];
D --> E[结束];
综上所述,gawk 是一个强大的工具,通过灵活运用变量、数组、匹配模式、结构化命令和格式化打印等功能,可以高效地处理和分析数据。
5. 综合示例与应用场景
5.1 数据筛选与统计示例
假设我们有一个包含学生成绩的数据文件
scores.csv
,格式如下:
姓名,数学,语文,英语
张三,85,90,88
李四,78,82,75
王五,92,95,93
我们可以使用 gawk 进行数据筛选和统计,例如筛选出数学成绩大于 80 分的学生,并计算他们的平均语文成绩。
$ gawk -F ',' 'NR > 1 && $2 > 80 {sum += $3; count++} END {if (count > 0) print "平均语文成绩: " sum / count; else print "无符合条件的学生"}' scores.csv
平均语文成绩: 92.5
上述代码中,
NR > 1
用于跳过标题行,
$2 > 80
筛选出数学成绩大于 80 分的学生,
sum
累加这些学生的语文成绩,
count
记录符合条件的学生数量,最后在
END
块中计算并输出平均语文成绩。
5.2 日志分析应用场景
在系统日志分析中,gawk 可以帮助我们提取关键信息。例如,有一个系统日志文件
system.log
,记录了用户的登录和退出信息:
2024-01-01 10:00:00 用户张三 登录
2024-01-01 10:30:00 用户张三 退出
2024-01-01 11:00:00 用户李四 登录
2024-01-01 11:45:00 用户李四 退出
我们可以使用 gawk 统计每个用户的登录时长:
$ gawk '{
if ($3 ~ /登录/) {
user = $4;
login_time[$4] = mktime(gensub(/[-: ]/, " ", "g", $1 " " $2));
} else if ($3 ~ /退出/) {
logout_time = mktime(gensub(/[-: ]/, " ", "g", $1 " " $2));
duration = logout_time - login_time[$4];
print user " 登录时长: " duration " 秒";
}
}' system.log
张三 登录时长: 1800 秒
李四 登录时长: 2700 秒
在这个示例中,我们使用
mktime
函数将日期时间字符串转换为时间戳,通过比较登录和退出的时间戳计算登录时长。
5.3 数据转换应用场景
有时候我们需要将数据从一种格式转换为另一种格式。例如,将上述
scores.csv
文件转换为 JSON 格式:
$ gawk -F ',' 'NR > 1 {
print "{\"姓名\": \"" $1 "\", \"数学\": " $2 ", \"语文\": " $3 ", \"英语\": " $4 "},"
} BEGIN {print "["} END {print "]"}' scores.csv
[
{"姓名": "张三", "数学": 85, "语文": 90, "英语": 88},
{"姓名": "李四", "数学": 78, "语文": 82, "英语": 75},
{"姓名": "王五", "数学": 92, "语文": 95, "英语": 93},
]
通过 gawk,我们可以方便地将 CSV 数据转换为 JSON 格式,便于后续的数据处理和传输。
6. 性能优化与注意事项
6.1 性能优化建议
-
减少不必要的循环
:在使用循环语句(如
for、while)时,确保循环次数尽可能少,避免不必要的重复计算。 - 合理使用数组 :关联数组虽然方便,但在处理大量数据时可能会占用较多内存。如果数据具有顺序性,可以考虑使用数值数组。
- 避免频繁的文件读写 :尽量一次性读取文件数据到内存中进行处理,避免多次打开和关闭文件。
6.2 注意事项
- 变量作用域 :gawk 中的变量作用域需要注意,特别是在嵌套循环和函数中,避免变量名冲突。
- 正则表达式性能 :复杂的正则表达式可能会影响性能,尽量使用简单有效的正则表达式进行匹配。
- 脚本可读性 :编写脚本时,注意代码的可读性和可维护性,合理使用注释和换行,避免将过多的代码写在一行。
7. 总结
gawk 作为一种强大的文本处理工具,在数据处理、日志分析、数据转换等领域有着广泛的应用。通过掌握 gawk 的变量操作、数组使用、匹配模式、结构化命令和格式化打印等功能,我们可以高效地完成各种数据处理任务。
在实际应用中,我们可以根据具体需求灵活运用这些功能,同时注意性能优化和代码的可读性。希望本文能帮助你更好地掌握 gawk 编程,提升数据处理能力。
下面是一个 mermaid 流程图,展示使用 gawk 进行数据处理的整体流程:
graph TD;
A[开始] --> B[读取数据文件];
B --> C[设置变量和数组];
C --> D[应用匹配模式筛选数据];
D --> E[使用结构化命令处理数据];
E --> F[格式化输出结果];
F --> G[结束];
同时,为了方便大家回顾,这里再次列出 gawk 中常用的格式说明符控制字母:
| 控制字母 | 描述 |
| ---- | ---- |
| c | 将数字显示为 ASCII 字符 |
| d | 显示整数值 |
| i | 显示整数值(与 d 相同) |
| e | 以科学记数法显示数字 |
| f | 显示浮点值 |
| g | 显示科学记数法或浮点数,取较短者 |
| o | 显示八进制值 |
| s | 显示文本字符串 |
| x | 显示十六进制值 |
| X | 显示十六进制值,但 A 到 F 使用大写字母 |
通过以上内容,相信你对 gawk 的高级编程有了更深入的理解和掌握,快去实践中运用这些知识吧!
超级会员免费看
37

被折叠的 条评论
为什么被折叠?



