awk 是一种功能强大的文本处理工具和编程语言,尤其适用于处理结构化文本数据(如日志、CSV文件等)。它的名字来源于其三位创始人的姓氏首字母:Aho、Weinberger 和 Kernighan。

核心概念

awk 的基本思想是:逐行扫描文件(或输入流),根据给定的“模式(Pattern)”执行对应的“动作(Action)”

其基本语法结构为:

awk 'pattern { action }' input_file
  • 如果省略 pattern,则对每一行都执行 action
  • 如果省略 { action },则默认动作是打印匹配 pattern 的行(即 { print $0 })。

1. 基本用法和常用选项

选项 含义
-F fs 指定输入字段分隔符(Field Separator),默认为空格和制表符。例如 -F ':'-F ","
-v var=value 定义一个变量并赋值,可以在 awk 脚本中使用。
-f scriptfile 从文件而不是命令行中读取 awk 脚本。

示例:

# 打印 /etc/passwd 文件的第一列(用户名)和第三列(用户ID)
awk -F ':' '{ print $1, $3 }' /etc/passwd

# 使用多个分隔符(先使用空格或逗号进行分割)
awk -F '[ ,]' '{ print $1, $2 }' file.txt

2. awk 程序结构:Pattern 和 Action

一个 awk 程序通常由一系列 pattern { action } 语句组成。

a) 特殊的 Pattern

awk 有一些特殊的 pattern,它们不是正则表达式,而是关键字:

Pattern 含义
BEGIN 在处理任何输入行之前执行一次。常用于初始化变量、打印表头。
END 在处理完所有输入行之后执行一次。常用于汇总和输出最终结果。
NR == n 仅处理第 n 行(NR 是内置变量,代表当前行号)。
NF > n 仅处理字段数(列数)大于 n 的行(NF 是内置变量,代表当前行的字段数)。

示例:

# 打印前加上表头,最后加上结束语
awk -F ':' 'BEGIN { print "User\tUID" } { print $1 "\t" $3 } END { print "Processing complete." }' /etc/passwd

# 只处理第5到10行
awk 'NR>=5 && NR<=10 { print $0 }' file.txt
b) 常见的 Pattern 类型
  1. 正则表达式 Pattern/regex/

    # 打印包含 "error" 或 "Error" 的行(忽略大小写)
    awk '/[Ee]rror/ { print $0 }' logfile.log
    # 另一种写法:使用 ~ 操作符
    awk 'tolower($0) ~ /error/ { print }' logfile.log
    
  2. 比较表达式 Pattern:使用比较运算符(==, !=, >, <, >=, <=

    # 打印第三列大于1000的行
    awk -F ':' '$3 > 1000 { print $1 }' /etc/passwd
    
  3. 范围 Patternpattern1, pattern2

    # 打印从包含 "START" 的行到包含 "END" 的行之间的所有内容
    awk '/START/, /END/ { print }' file.txt
    
  4. 组合 Pattern:使用逻辑运算符 && (与), || (或), ! (非)

    # 打印第一列为 "root" 且第三列为0的行
    awk -F ':' '$1 == "root" && $3 == 0' /etc/passwd
    

3. 内置变量

awk 提供了许多有用的内置变量:

变量 含义
$0 当前整行的内容。
$1, $2, ... $NF 当前行的第1、第2、直到最后一个字段。
NF (Number of Fields) 当前行的字段总数。$NF 代表最后一个字段。
NR (Number of Records) 当前处理的行号(累计 across files)。
FNR (File Number of Records) 当前文件中的行号(对每个文件重置为1)。
FS (Field Separator) 输入字段分隔符,默认为空格。可通过 -F 选项设置。
OFS (Output Field Separator) 输出字段分隔符,默认为空格。例如 awk -v OFS="," '{print $1,$2}'
RS (Record Separator) 输入记录(行)分隔符,默认为换行符 \n
ORS (Output Record Separator) 输出记录(行)分隔符,默认为换行符 \n
FILENAME 当前正在处理的文件名。

示例:

# 打印每行的行号和最后一个字段
awk '{ print NR, $NF }' file.txt

# 打印文件总行数
awk 'END { print NR }' file.txt

# 打印字段数大于2的行的第一个字段
awk 'NF > 2 { print $1 }' file.txt

4. 动作(Action)和函数

动作是在 {} 中执行的语句块,最常见的动作是 printprintf

a) printprintf
  • print:自动在输出项之间加上 OFS,最后加上 ORS
  • printf:格式化输出,类似于 C 语言的 printf
# print 示例
awk '{ print "User:", $1, "UID:", $3 }' /etc/passwd

# printf 示例(更精确控制格式)
awk -F ':' '{ printf "Username: %-15s UID: %d\n", $1, $3 }' /etc/passwd
# %-15s: 左对齐,占15个字符的字符串
# %d: 十进制整数
# \n: 换行
b) 算术和字符串操作

awk 内置支持变量和算术运算。

# 计算第一列数字的总和
awk '{ sum += $1 } END { print sum }' numbers.txt

# 字符串连接直接使用空格即可
awk '{ print $1 "-" $2 }' file.txt # 输出 first-second
c) 流程控制

awk 支持 ifelsewhilefor 等流程控制语句,语法类似 C 语言。

# 条件判断
awk '{ if ($3 > 1000) print $1 " is a user"; else print $1 " is a system account" }' /etc/passwd

# 循环
awk '{ for (i=1; i<=NF; i++) print "Field", i, ":", $i }' file.txt
d) 内置函数

awk 提供了丰富的内置函数。

  • 数学函数sin(), cos(), sqrt(), log(), int()
  • 字符串函数
    • length(str): 返回字符串长度。
    • substr(str, start, len): 提取子串。
    • index(str, substr): 查找子串位置。
    • split(str, arr, fs): 将字符串分割到数组。
    • gsub(rgex, repl, str): 全局替换。
    • tolower(str), toupper(str): 转换大小写。
# 将第一列转换为大写
awk '{ print toupper($1) }' file.txt

# 替换所有的 "old" 为 "new"
awk '{ gsub(/old/, "new"); print }' file.txt

5. 高级用法:数组

awk 支持关联数组(字典或哈希表),索引可以是数字或字符串。

# 统计第一列每个值出现的次数
awk '{ count[$1]++ } END { for (item in count) print item, count[item] }' data.txt

# 计算每个用户的进程总数(例如来自 `ps aux` 的输出)
ps aux | awk '{ user[$1]++ } END { for (u in user) print u, user[u] }'

6. 综合示例

假设我们有一个名为 sales.txt 的文件,内容如下:

Alice,100,2023-10-25
Bob,150,2023-10-25
Alice,200,2023-10-26
Charlie,50,2023-10-26
Bob,300,2023-10-27

任务:计算每个人的总销售额。

awk -F ',' '{ sales[$1] += $2 } END { for (name in sales) print name, sales[name] }' sales.txt

输出可能为

Alice 300
Bob 450
Charlie 50

总结

awk 是一个非常强大的工具,其核心是 “模式-动作” 对。掌握了基本结构、内置变量和函数后,你可以用它高效地完成各种文本处理、数据提取和报告生成任务。对于更复杂的逻辑,可以编写在 BEGINEND 块中。

对于日常使用,记住这个“万能”结构会非常有帮助:

awk 'BEGIN { /* 初始化 */ } /pattern/ { /* 处理每一行 */ } END { /* 收尾工作 */ }' input.txt
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐