文本处理工具awk

awk简介

awk是一种用于处理文本数据的工具,可以从文本文件或者标准输入、一个或多个文件中提取和处理数据。
awk以逐行扫描方式处理数据,从第一行到最后一行以查找匹配某个特定模式的文本行,并且对这些文本行执行指定动作。
主要用于文本的分析和格式化,尤其适合处理表格数据。

典型应用场景包括:

  1. 日志分析(提取特定事件、错误统计)
  2. 数据清洗(转换格式、过滤无效记录)
  3. 报表生成(计算总和、平均值,生成表格)

awk格式

awk基本格式如下:

awk '条件 { 动作 }' 文件        # 条件匹配时执行动作
awk '{ 动作 }' 文件            # 无条件执行动作
awk '条件' 文件                # 仅输出匹配行
  • 条件: 可以是一个正则表达式,也可以是一个字符串或数字,用于匹配文本中的某个字段或整行
  • 动作: 是在匹配到的文本上执行的操作,可以是一个或多个命令,多个命令之间用换行或分号 ; 分隔
  • 文件: 是输入文件的名称,没有文件参数时从标准输入读取数据

awk还支持在处理数据前和处理数据后运行命令,完整格式如下:

awk 'BEGIN {初始化操作} 条件 {处理动作} END {收尾操作}' 文件
  • 用法示例:

    创建test1文件,写入以下原始数据:

    Alice 25 Female New York
    Bob 30 Male Los Angeles
    Charlie 22 Male Chicago
    David 35 Male Houston
    Eve 28 Female Miami

    完整的awk用法示例:

    # [计算] 累加第二列数值并输出总和awk 'BEGIN { sum = 0 } { sum += $2 } END { print "Total Age:", sum }' test1
    Total Age: 105

10.text-processing-tools-awk-2023-04-24-09-42-32

awk工作原理

  1. awk对文件或者标准输入的内容一次只处理一行,将获取到的这一行数据赋给变量$0
  2. 这一行的内容按awk内部变量FS定义的分隔符(缺省为空格,包括制表符tab)分解成多个字段,每一个字段存储在从$1开始的变量中。

以以下数据为例:

Alice 25 Female New York
Bob 30 Male Los Angeles
Charlie 22 Male Chicago
David 35 Male Houston
Eve 28 Female Miami

以下awk命令的运行结果:

# [提取] 输出第 1 列和第 3 列 (默认空格分隔)awk '{print $1,$3}' test1
Alice Female
Bob Male
Charlie Male
David Male
Eve Female
  1. awk首先将第一行数据存放在变量$0中进行处理。
  2. $0中的数据以空格为分隔符(内部变量FS所定义)进行分解,分别存放在从$1开始的内部变量中。
  3. 在输出结果的每一行中第一个字段是存放在$1中的数据,第二个字段是存放在$3中的数据。
  4. awk命令中的逗号映射为一个内部变量-输出字段分隔符OFS(默认为空格)两个字段之间由空格间隔。
  5. 处理完一行数据后读取下一行数据到$0中进行处理直到所以数据处理完毕。

作业1:以下是服务器产生的日志文件,将其保存到文件test2中,使用awk命令使其仅输出时间戳和CPU占用两列信息,上传awk命令的执行结果。

[2023-08-15 09:00] CPU:35% MEM:1800MB DISK:/ 75%
[2023-08-15 09:05] CPU:82% MEM:2100MB DISK:/ 78%
[2023-08-15 09:10] CPU:91% MEM:2300MB DISK:/ 80%

通过管道符号从命令读取数据

# [筛选] 查找包含 "bash" 的行ps -aux|awk '/bash/'
root           1  0.0  0.0  18228  3012 pts/0    Ss+  Apr22   0:00 /bin/bash
root          11  0.0  0.0  18232  3240 pts/1    Ss   13:36   0:00 /bin/bash
ubuntu       269  0.0  0.0  18228  3144 pts/1    S    13:39   0:00 bash

以上awk命令通过管道符号|从前面一个命令的输出结果从读取数据进行处理。

从文件中读取数据和命令

# [匹配] 简单字符串匹配awk '/Alice/' test1
Alice,25,Female,New York
# [匹配] 包含 "Male" 的行awk '/Male/' test1
Bob,30,Male,Los Angeles
Charlie,22,Male,Chicago
David,35,Male,Houston

以上awk命令指定了file参数,awk会从文件中以行为单位读取数据并且使用条件进行数据匹配,由于没有指定动作参数,awk将匹配到的数据直接输出到标准输出。

awk和sed一样可以从文件中读取命令:

awk -f script_file.awk test1

格式化输出

print函数

print函数用于打印不需要特别编排格式的简单输出。

# [输出] 无逗号:字段直接拼接awk '{print "Name:" $1 "Age:" $2}' test1
Name:AliceAge:25
Name:BobAge:30
Name:CharlieAge:22
Name:DavidAge:35
Name:EveAge:28
# [输出] 有逗号:使用 OFS (空格) 分隔awk '{print "Name:" $1, "Age:" $2}' test1
Name:Alice Age:25
Name:Bob Age:30
Name:Charlie Age:22
Name:David Age:35
Name:Eve Age:28

逗号,表示内部变量OFS(用来表示输出字段的分隔符,默认为空格)。
print还支持以下转义序列:

  • \b:退格符(ASCII 008)
  • \t:制表符(ASCII 009)
  • \n:换行符(ASCII 010)
  • \f:换页符(ASCII 012)
  • \r:回车符(ASCII 013)

print中使用转义序列进行数据输出:

# [格式化] 使用制表符 \tawk '{print "Name:" $1 "\tAge:" $2}' test1
Name:Alice      Age:25
Name:Bob        Age:30
Name:Charlie    Age:22
Name:David      Age:35
Name:Eve        Age:28

printf函数

printf函数能够进行更加精细的格式化输出,用法和C语言中printf函数的用法一样。语句中包含一个加引号的控制串,控制串中包含若干格式说明和修饰符。控制串后面跟一个逗号,之后是一列由逗号分隔的表达式。printf会根据控制串来编排表达式的格式。
printfprint不同的是printf在行尾不会自动换行,如果要换行必须使用转义字符\n

# [printf] 行尾不会自动换行awk '{printf "[Name:%-10s]\tAge:%s", $1, $2}' test1
[Name:Alice     ]        Age:25[Name:Bob       ] Age:30[Name:Charlie   ]      Age:22[Name:David     ] Age:35[Name:Eve       ] Age:28
# [printf] 手动添加 \n 换行awk '{printf "[Name:%-10s]\tAge:%s\n", $1, $2}' test1
[Name:Alice     ]       Age:25
[Name:Bob       ]       Age:30
[Name:Charlie   ]       Age:22
[Name:David     ]       Age:35
[Name:Eve       ]       Age:28

记录与字段

记录

在awk中每一行数据称为一条记录,以换行符结束。

  • 记录分隔符:默认情况下,输入和输出记录的分隔符都是换行符,分别保存在内部变量ORSRS中。
  • 变量$0表示当前整条记录,变量NR表示当前记录的记录号,每处理一条记录,NR值加一。
# [记录号] 通过 NR 变量输出行号awk '{print NR,$0}' test1
1 Alice 25 Female New York
2 Bob 30 Male Los Angeles
3 Charlie 22 Male Chicago
4 David 35 Male Houston
5 Eve 28 Female Miami

字段

每一条记录都是由称为字段的词组成,默认情况下字段之间用空白符(空格或者制表符)分隔。NF变量保存每个记录的字段数量。

# [字段数] 通过 NF 变量输出每行字段数量awk '{print NF,$0}' test1
5 Alice 25 Female New York
5 Bob 30 Male Los Angeles
4 Charlie 22 Male Chicago
4 David 35 Male Houston
4 Eve 28 Female Miami

字段分隔符

字段分隔符由内部变量FS保存,可以在命令中通过-F选项修改FS的值。
先使用sed命令将test文件中的空格替换为逗号:

sed -i 's/ /,/g' test1
❯ cat test1
Alice,25,Female,New,York
Bob,30,Male,Los,Angeles
Charlie,22,Male,Chicago
David,35,Male,Houston
Eve,28,Female,Miami

然后使用awk并通过-F选项替换分隔符进行输出:

# [错误示例] 默认空格分隔,无法正确处理逗号分隔的文件awk '{print "Name:" $1, "Age:" $2}' test1
Name:Alice,25,Female,New,York Age:
Name:Bob,30,Male,Los,Angeles Age:
Name:Charlie,22,Male,Chicago Age:
Name:David,35,Male,Houston Age:
Name:Eve,28,Female,Miami Age:
# [正确示例] 使用 -F 修改分隔符为逗号awk -F, '{print "Name:" $1, "Age:" $2}' test1
Name:Name Age:Age
Name:Alice Age:25
Name:Bob Age:30
Name:Charlie Age:22
Name:David Age:35
Name:Eve Age:28

匹配模式与正则表达式

匹配模式

awk中的模式可以是一个正则表达式或者用来判定条件真伪的表达式。

使用Alice进行数据匹配:

cat test1
Alice 25 Female New York
Bob 30 Male Los Angeles
Charlie 22 Male Chicago
David 35 Male Houston
Eve 28 Female Miami
# [匹配] 搜索包含 Alice 的行awk '/Alice/' test1
Alice 25 Female New York

使用条件表达式进行数据匹配:

cat test1
Alice 25 Female New York
Bob 30 Male Los Angeles
Charlie 22 Male Chicago
David 35 Male Houston
Eve 28 Female Miami
# [过滤] 筛选第 2 列 (年龄) 小于 25 的记录awk '$2 < 25' test1
Charlie 22 Male Chicago

思考:实用awk命令按照以下格式输出年龄最大的人员信息:

姓名:某某某,年龄:xx

正则表达式模式与操作

使用正则表达式模式^A.*\b进行数据匹配,然后通过print函数进行格式化输出:

cat test1
Alice 25 Female New York
Bob 30 Male Los Angeles
Charlie 22 Male Chicago
David 35 Male Houston
Eve 28 Female Miami
❯ awk '/^A.*\b/{print "Name:" $1}' test1
Name:Alice

格式化输出test文件中所有以大写字母A-C开头的行:

cat test1
Alice 25 Female New York
Bob 30 Male Los Angeles
Charlie 22 Male Chicago
David 35 Male Houston
Eve 28 Female Miami
❯ awk '/^[A-C]+.*/{print "Name:" $1}' test1
Name:Alice
Name:Bob
Name:Charlie

作业2:使用awk,通过处理/etc/passwd来显示各用户使用的shell,按照以下格式输出。

User:root Shell:/bin/bash
User:daemon Shell:/usr/sbin/nologin
User:bin Shell:/usr/sbin/nologin
User:sys Shell:/usr/sbin/nologin
User:sync Shell:/bin/sync
User:games Shell:/usr/sbin/nologin
User:man Shell:/usr/sbin/nologin
User:lp Shell:/usr/sbin/nologin
User:mail Shell:/usr/sbin/nologin
User:news Shell:/usr/sbin/nologin