53、高级 gawk 编程指南

高级 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 的高级编程有了更深入的理解和掌握,快去实践中运用这些知识吧!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值