awk 从入门到入土

awk 从入门到入土

何为 awk ?

awk 是一种用于文本处理、数据提取分析和报告常用的 linux 工具(命令)。与 sed 和 grep 一样,在日常编程和使用 linux 操作系统中,它是提升效率的法宝。

awk 处理的数据可以来自标准输入(stdin)、文件,或其它命令的输出。它支持用户自定义函数和动态正则表达式等功能,是linux/unix下的一个强大编程工具。它在命令行中使用,但更多是作为脚本来使用。awk 内有完备的“语言”,可以写出数组、函数、分支等复杂结构,且语法与 C 语言的相通之处。相比 sed grep 命令,灵活性是 awk 最大的优势,但其包含了复杂语法、正则表达式、内置变量(函数)也让很多人望而生畏。

另外,该工具之所以叫 AWK 是因为其取了三位创始人 Alfred Aho,Peter Weinberger, 和 Brian Kernighan 的 Family Name 的首字符。

awk 到底能做什么?

awk 功能强大,特点繁杂,若开篇介绍用法会让该博文显得杂乱无章,让人毫无阅读兴趣。我尝试抛开复杂的语法和命令格式,先利用几个例子让读者了解一下 awk 的工作方式和功能是怎样的。相信你一定会被 awk 强大的信息提取能力所折服。当你熟练掌握 awk 后,就再也不会烦恼于数据的格式化输出和信息提取了😄。

awk 对文件操作

  1. 逐行扫描文件。awk 的基本功能是搜索/匹配文件中包含特定模式的文本。当其中的内容匹配到了该模式时,awk 会在上执行指定的操作。awk 基本以行为处理单元,以这种方式一行一行地处理文本,直到遇到文件的末尾。

    1
    2
    3
    4
    5
    例1:打印文件的每行的第一列(域)
    > awk '{print $1}' filename

    例2:打印文件的每一行
    > awk '{print $0}' filename

    print 是 awk 中最常用的操作,可打印出后面的字符,若有多个变量,用’,'连接。虽然脚本中只写了对一行的操作,但由于 awk 会以逐行的方式遍历整个文本,因此最终该命令会打印出每一行的结果。

  2. 将每一行输入拆分为字段。awk 逐行扫描文件后,再对行中的列(域)做匹配。可以使用 $n 来表示第n列的字符,而 $0 表示整一行。内置变量 $NF 表示字段总数,因此 $NF 可表示倒数第一列。对列的分割默认是空格,但也可以通过改变内置变量 FS 来改变分割符号。

    1
    2
    3
    3:从 /etc/passwd 文件中,按照":"分割打印出第2列和倒数第一列、倒数第二列:
    > awk -F":" '{print $2,$(NF),$(NF-1)}' /etc/passwd (或)
    > awk 'BEGIN{FS=":"} {print $2,$(NF),$(NF-1)}' /etc/passwd

    BEGIN 块表示该脚本块需要在 awk 逐行遍历文件前就执行,且只执行一次,通常用于初始化内部变量或计算数据。与之相反,END 块只能在awk 逐行遍历文件后执行,且执行一次

  3. 比较输入行/字段与模式。比较或匹配模式通常要涉及到正则表达式的相关知识,关于正则表达式可参考菜鸟教程。但 awk 还提供了类似 C 语言中的判断指令,从而做出更复杂的条件判断和算术逻辑。甚至可以完全抛弃文件,单独做一些复杂计算。

    1
    2
    3
    4
    5
    4:找到 test.txt 中最后一列大于 5000 的列并打印出来:
    > awk '{if ($NF > 5000) print $NF}' test.txt

    5:使用 awk 命令计算 exp(5)
    > awk 'BEGIN {param = 5; result = exp(param); printf "Result is %f.\n", result}'

    awk 有大量的内置变量和内置函数,具体介绍在后小节。

  4. 对匹配的行执行动作。awk主要的操作就是print,将数据按规定的格式打印出来。但需要注意,awk 命令一般不直接修改文件,只能将输出信息重定向到某一个文件中(非源文件)。

可用此图再回顾一下 awk 的四步流程。总结一下,awk 适用于数据文件转换和编制格式报告,并且能做到:1、格式化地输出信息。2、各种复杂的算术逻辑运算和字符串操作。3、配合逐行特性,实现条件和循环结构。

awk 命令格式和选项

语法形式

使用 awk 命令时,一般遵循下面两种形式书写:

1
2
> awk [options] 'script' [var=value] [file(s) name]
> awk [options] -f scriptfile [var=value] [file(s) name]

接下来的几个小节开始介绍 awk 各个部分的使用规则:

常用命令选项 —— [options]

  • -F <fs> 或 --field-separator <fs> :fs 为指定输入分隔符,也可以是字符串或正则表达式。默认的分隔符是连续的空格或制表符
  • -v <var>=<value> 赋值一个用户定义变量,将外部变量传递给 awk
  • -f <scripfile> 从脚本文件中读取awk命令
  • -m[fr] <val]> 对 val 值设置内在限制,-mf选项限制分配给val的最大块数目;-mr选项限制记录的最大数目。这两个功能是Bell实验室版awk的扩展功能,在标准awk中不适用。

awk 模式和操作 —— ‘script’

首先回顾一下前文所说:

awk 的基本功能是搜索/匹配文件中包含特定模式的文本。当其中的内容匹配到了该模式时,awk 会在上执行指定的操作。awk 基本以行为处理单元,以这种方式一行一行地处理文本,直到遇到文件的末尾。

模式操作无疑是 awk 命令中最重要的部分,而它们都会在 ‘script’ 处集中表达,其中

模式

模式可以是以下任意一个:

  • 正则表达式:使用通配符的扩展集。

    1
    2
    例6:从 employee.txt 文件中找到含有 manager 的行并打印
    > awk '/manager/ {print}' employee.txt

    ‘/manager/’ 即要匹配字符串中含有 manager 的子串。找到后打印文件所在行,{print} 默认打印一行的所有列。

  • 关系表达式:使用运算符进行操作,可以是字符串或数字的比较测试。

    1
    2
    7:打印 log.txt 中第一列大于2并且第二列等于 'Are' 的行的前三列:
    > awk '$1>2 && $2=="Are" {print $1,$2,$3}' log.txt

    $1>2 && $2==“Are” 表示第一列大于2且第二列为 ‘Are’,可以仅写一个条件,也可以完整地写出 if 语句结构,见下例。

  • 模式匹配表达式:用运算符~(匹配)和!~(不匹配)。

    1
    2
    例8:打印 test.txt 文件中第二列,并匹配以80开头并以80结束的行:
    > awk '{if ($2 ~ /^80$/) print}' test.txt
  • BEGIN 语句块、pattern 语句块、END 语句块。

    1
    2
    9:找到 test.txt 文件中最长行的所占的字符数
    > awk 'BEGIN{max=0} {if (length($0) > max) max=length($0)} END{print max}' test.txt

    由三个语句块组成的模式甚至可看成一个简单 C 程序。length() 是 awk 的内置函数。更多函数可见菜鸟教程的整理

由于 awk 的逐行处理文本的特性,很多重要的全局信息往往通过内置变量表达,在书写 awk 命令时常需要快速查表并使用。限于篇幅不详细介绍内置变量的使用了,不太懂的话可以参考这篇博客

内置变量 含义
NF 字段个数,(读取的列数)
NR 记录数(行号),从1开始,新的文件延续上面的计数,新文件不从1开始
FNR 读取文件的记录数(行号),从1开始,新的文件重新从1开始计数
FS 输入字段分隔符,默认是空格
OFS 输出字段分隔符 默认也是空格
RS 输入行分隔符,默认为换行符
ORS 输出行分隔符,默认为换行符
FILENAME 输入的文件名
1
2
10:读出 /etc/passwd 文件的第三和第四列,并用等号连接打印出来
> awk -F':' 'BEGIN{OFS="=";} {print $3,$4;}' /etc/passwd

操作

操作由一个或多个命令、函数、表达式组成,之间由换行符或分号隔开,并位于大括号内,主要部分是:

变量或数组赋值
输出命令
内置函数
控制流语句

awk 的变量 —— [var=value]

除了之前提到的内置变量外,用户在使用 awk 时也可以自定义变量:var=value。当然也可以直接使用 awk 语言定义

1
2
3
4
5
11:定义变量var并将其打印出来
> awk -v var="test" 'BEGIN{print var}'

12:直接在操作中给变量赋值
> awk 'BEGIN{var="test"; print var}'

回顾例9,变量的设置可以帮助程序员用类C的语法完成更加复杂的任务。

1
2
9:找到 test.txt 文件中最长行的所占的字符数
> awk 'BEGIN{max=0} {if (length($0) > max) max=length($0)} END{print max}' test.txt

awk 常常与其他 shell 指令合用,在传递数据时也需要通过变量:

1
2
3
4
例13:读取文件 a.txt 的行数,并用 awk 打印出来
> num=$(cat a.txt | wc -l)
> awk -v n=$num 'BEGIN{print n}' 或者
> awk 'BEGIN{print '"$num"'}'

awk 从入门到入土
https://dingfen.github.io/2022/07/19/2022-7-19-awk/
作者
Bill Ding
发布于
2022年7月19日
更新于
2024年4月9日
许可协议