// Linux · 2025-01-21

awk 从入门到进阶(上)

前言

在信息化时代,文本数据的处理早已成为日常运维、数据分析和开发工作中不可或缺的一部分。无论是从服务器日志中提取关键信息、解析配置文件的内容,还是对海量数据进行筛选、统计和分析,高效的文本处理工具都直接决定了任务完成的效率与质量。在众多文本处理工具中,awk 凭借其强大的功能、简洁的语法和广泛的适用性,成为 Unix/Linux 系统中当之无愧的明星工具。作为操作系统自带的标准组件,awk 不仅在命令行中游刃有余,还能在数据分析、自动化脚本编写和报告生成等场景中大显身手。

对于经常需要处理文本数据的从业者来说,awk 是一个值得深入学习和掌握的工具。无论你是初学者希望快速上手,还是经验丰富的开发人员想要进一步优化工作流程,awk 都能为你提供高效的解决方案。本文将从 awk 的基础用法入手,逐步深入到其核心功能和实用技巧,带你在最短时间内掌握 awk 的基本技能,并将其应用到实际工作中。掌握了这些基础知识后,你将为后续的进阶学习和应用打下坚实的基础。


AWK 的基本概念

awk 是一种专门为文本处理设计的脚本语言,其名称来源于三位开发者——Alfred Aho、Peter Weinberger 和 Brian Kernighan 的姓氏首字母。自诞生以来,awk 就以其简洁高效的特质赢得了广泛的青睐。它的设计初衷是为了简化对结构化文本的处理任务,尤其是在处理日志、CSV 文件或其他格式化数据时表现出色。

  • 核心理念:awk 的工作方式是逐行读取输入文件,按照用户指定的模式进行匹配,并对匹配的行执行相应的操作,最终输出处理结果。这种“逐行处理”的机制使其非常适合处理大规模文本数据。
  • 优势:
    • 语法简洁:awk 的命令通常可以用一行代码完成复杂的任务,特别适合快速开发和临时使用。
    • 功能强大:支持复杂的逻辑运算、数学计算和字符串操作,足以应对多样化的需求。
    • 灵活性高:与 Unix/Linux 中的其他工具(如 grep、sed、sort 等)无缝集成,形成强大的命令行流水线。

无论是提取特定字段、统计数据,还是生成格式化报告,awk 都能以其独特的优势胜任。如果你尚未接触过 awk,或者对其功能仅停留在表面,那么接下来的内容将为你打开一扇通往高效文本处理的大门。


语法结构:模式与动作

awk 的核心逻辑建立在“模式与动作”这一基本结构之上。简单来说,awk 将输入数据视为一系列记录(默认情况下,每行是一个记录),每个记录又被分割成若干字段(默认以空格或制表符分隔)。它的程序由模式(Pattern)和动作(Action)两部分组成,模式决定哪些记录需要处理,动作则定义对这些记录的具体操作。其基本语法如下:

awk 'pattern1 {action1} pattern2 {action2}' file

在这条命令中,pattern1 {action1} 和 pattern2 {action2} 是两个独立的“模式-动作”对,awk 会按顺序逐行匹配并执行对应的动作。如果某一行匹配了多个模式,则会依次执行所有匹配的动作。

模式(Pattern)

模式是 awk 的筛选机制,用于决定哪些行需要被处理。常见的模式类型包括:

  • 正则表达式:用于匹配字段中的特定模式。例如,/error/ 可以匹配包含 “error” 的行。
  • 关系表达式:通过比较字段值进行筛选,如 $1 > 100 表示选择第一个字段大于 100 的行。
  • 特殊模式:
    • BEGIN:在处理任何输入之前执行,常用于初始化变量或设置环境。
    • END:在所有输入处理完毕后执行,用于汇总结果或输出总结。

动作(Action)

动作是对匹配记录的具体操作,通常由大括号 {} 包裹。常见的动作包括:

  • 打印操作:如 print $1 输出第一个字段,或 printf "%s\n", $1 进行格式化输出。
  • 变量赋值:如 sum += $1 将第一个字段的值累加到变量 sum。
  • 控制语句:支持 if、while、for 等条件和循环语句。
  • 内置函数:如 gsub(全局替换)、split(字符串分割)等。

模式与动作的省略

awk 的灵活性还体现在模式或动作可以省略的情况下:

  1. 省略模式
    如果省略模式,则动作会应用于所有输入行。例如:

    awk '{print $1}' file

    这条命令会打印文件 file 中每一行的第一个字段,因为没有指定模式,所有行都被匹配。

    如果有多个模式-动作对,可以组合使用。例如:

    awk '{print $1} $1 > 100 {print $2}' file
    • {print $1}:对每一行打印第一个字段。
    • $1 > 100 {print $2}:仅对第一个字段大于 100 的行打印第二个字段。
  2. 省略动作
    如果省略动作,awk 默认执行 {print $0},即打印匹配的整行。例如:

    awk '$1 > 100' file

    这条命令会输出 file 中第一个字段大于 100 的所有行。

    更简洁的方式是用 1 表示默认打印整行,例如:

    awk '1' file

    这会打印文件中的每一行,因为 1 恒为真,等价于 {print $0}。

内置工作流

awk 的执行过程分为三个阶段:

  1. BEGIN 块:在读取任何输入之前运行,用于初始化变量、设置分隔符或打印表头。例如:

    awk 'BEGIN {print "开始处理文件..."}' file

    即使没有输入文件,BEGIN 块也会执行一次,这在调试或测试时非常有用。

  2. 主程序块:逐行处理输入文件的内容,是 awk 的核心处理阶段。

  3. END 块:在所有行处理完毕后运行,常用于计算总和、平均值或输出总结。例如:

    awk 'BEGIN {print "开始"} {print $1} END {print "结束"}' file

    这条命令会先输出“开始”,然后打印每一行的第一个字段,最后输出“结束”。

通过灵活运用模式与动作,awk 可以轻松实现从简单筛选到复杂计算的各种任务。


字段分隔符

awk 的一个显著特点是其对字段的处理能力。要熟练使用 awk,掌握字段分隔符的设置和应用至关重要。默认情况下,awk 将空格或制表符作为字段分隔符,但用户可以通过 -F 选项自定义分隔符,从而适配不同的数据格式。

基本用法

例如,Linux 系统中的 /etc/passwd 文件使用冒号 : 分隔字段,可以用以下命令提取用户名(第一个字段):

awk -F ":" '{print $1}' /etc/passwd

这里,-F ":" 将冒号指定为字段分隔符,$1 表示每行的第一个字段。

正则表达式分隔符

awk 的强大之处在于支持正则表达式作为分隔符。例如:

awk -F '[: ]' '{print $1}' file

这条命令将冒号 : 或空格作为分隔符,适用于字段分隔符不固定的场景。如果需要匹配一个或多个连续的分隔符,可以使用 +:

awk -F '[: ]+' '{print $1}' file

这会将一个或多个冒号或空格视为一个分隔符,避免空字段的出现。

参数传递的灵活性

在指定分隔符时,awk 的命令行选项非常灵活:

  • 可以用单引号包裹,如 -F ':';
  • 可以用双引号包裹,如 -F ":";
  • 甚至直接写为 -F: 或 -F :,选项与参数之间不强制要求空格。

为了避免特殊字符或空格引发歧义,建议在复杂分隔符中使用引号。例如:

awk -F "[,|]" '{print $1}' file

这条命令将逗号 , 或竖线 | 作为分隔符,使用方括号表示正则表达式中的字符集。


变量使用

变量是 awk 的重要组成部分,能够极大地增强其灵活性和功能性。变量在使用前不需要声明。在 awk 中,变量的使用分为内置变量和自定义变量两大类。

内置变量

awk 提供了一系列内置变量,方便用户访问和操作文本数据。以下是常用内置变量及其含义:

变量 描述
$0 当前行的完整内容
$1,$2 当前行的第 N 个字段
NF 当前行的字段总数
NR 当前处理的行号(全局累计)
FNR 当前文件中的行号
FS 输入字段分隔符
OFS 输出字段分隔符

例如,可以用 NF 获取字段数:

awk '{print NF}' file

这条命令会输出每一行的字段总数。

又如,使用 OFS 设置输出分隔符:

awk 'BEGIN {OFS=":"} {print $1, $2}' file

这条命令将第一个和第二个字段用冒号 : 分隔输出。

变量赋值

awk 支持多种变量赋值方式,以满足不同场景的需求:

  1. 使用 -v 参数传递外部变量
    通过 -v 选项,可以将 Shell 中的变量传递给 awk:

    awk -v num=10 '{print num}' file

    这里,变量 num 被赋值为 10,并在每一行输出。

  2. 在 awk 内部动态赋值
    变量可以在处理过程中动态创建和修改:

    awk '{sum += $1} END {print sum}' file

    这条命令会计算所有行第一个字段的总和,并在最后输出。

引用外部变量

在 awk 中引用 Shell 变量时,需要注意 Shell 对引号的解析规则:

  • 单引号:内容作为字面量传递,不展开变量。
  • 双引号:支持变量展开和命令替换。

例如:

num=5
awk '{print '$num'}' file

在这条命令中,Shell 会先将 $num 展开为 5,然后传递给 awk,最终每行输出 5。注意这里的单引号是分段的,$num 位于单引号之外,因此会被 Shell 解析。

使用管道与 getline

awk 可以通过 getline 函数从外部命令读取数据。例如:

awk 'BEGIN {while ("date" | getline) print $0}'

这条命令调用 date 命令,并将输出传递给 awk,最终打印当前日期时间。getline 的灵活性使其在动态数据处理中非常实用。

输出到文件

假设有一个数据表如下:

+-----------+----------+-----------------+
| id_people | uzer     | pazz            |
+-----------+----------+-----------------+
|        16 | sfdfdsml | ixpeqdsfsdfdsfW |
|        44 | yuio     | ixpgbvcbvcbeqdW |

可以用以下命令将用户名和密码分别写入文件:

awk -F '[| ]+' '{print $3 > "user.txt"; print $4 > "pass.txt"}' file

这里,-F '[| ]+' 表示以一个或多个竖线或空格作为分隔符,> 将输出重定向到指定文件。

注意事项

  1. 未定义变量的默认值
    在 awk 中,未定义的变量默认值为 0(数字上下文)或空字符串(字符串上下文)。例如:

    awk '{print x}' file

    因为 x 未定义,因此它的默认值是空字符串,awk 会对每一行都打印空字符串,即输出一个空行。

  2. 正则表达式匹配
    模式匹配必须使用正则表达式,例如:

    awk '$1 ~ /pattern/' file

    错误示例:

    awk '$1 ~ pattern' file

    这里 pattern 被视为变量而非字符串,应改为 $1 ~ "pattern"。


小结

通过本文的系统学习,我们从 awk 的基本概念入手,详细探讨了其语法结构、字段分隔符的设置以及变量的使用方法。接下来的进阶内容将进一步挖掘 awk 的高级特性,助你在数据处理领域更上一层楼。敬请期待!

原文 https://blog.csdn.net/2301_79518550/article/details/145211759