awk 指令。它远不止是一个简单的文本切割工具,而是一门功能完整的模式扫描与处理语言。
一、核心思想与工作原理
awk 的核心思想是:逐行扫描输入(文件或标准输入),根据指定的“模式”匹配行,然后对匹配的行执行对应的“动作”。
它的工作流程可以概括为:
- 读取:从输入流中读取一行。
- 分割:将该行根据字段分隔符(默认是任何空格,包括 Tab 和空格)分割成多个字段。这些字段分别用
$1,$2, …,$n来引用。整行用$0引用。 - 模式匹配:检查该行是否与代码中提供的模式相匹配。
- 执行动作:如果匹配成功,则执行模式后面花括号
{}中的动作。 - 循环:重复步骤 1-4,直到处理完所有行。
一个最基本的 awk 程序结构如下:
pattern { action }
pattern { action }
...
二、基本语法与常用用法
1. 命令行调用方式
# 1. 直接命令行模式
awk 'pattern { action }' input_file
# 2. 使用脚本文件模式 (适合复杂的awk程序)
awk -f script.awk input_file
# 3. 处理前和执行后的操作 (BEGIN 和 END 块)
awk 'BEGIN { action_before_processing } pattern { action } END { action_after_processing }' input_file
2. 内置变量
awk 的强大之处在于它提供了丰富的内置变量,让你能轻松访问和处理数据。
| 变量名 | 含义 | 默认值 |
|---|---|---|
FS | 输入字段分隔符 | 空格 |
OFS | 输出字段分隔符 | 空格 |
RS | 输入记录(行)分隔符 | 换行符 \n |
ORS | 输出记录(行)分隔符 | 换行符 \n |
NF | 当前行的字段数量 | - |
NR | 当前处理的行号(所有文件累计) | - |
FNR | 当前文件的行号(对每个文件单独计数) | - |
FILENAME | 当前正在处理的文件名 | - |
$0 | 当前行的全部内容 | - |
$1, $2, ... $n | 当前行的第1, 2, … n个字段 | - |
示例:
# 打印第一和第三个字段,用逗号分隔
awk '{print $1 "," $3}' file.txt
# 打印每一行及其字段数量
awk '{print NR ": " $0 " (" NF " fields)"}' file.txt
# 处理以冒号分隔的文件 (如 /etc/passwd)
awk -F: '{print $1 "\'s home is " $6}' /etc/passwd
# 等价于
awk 'BEGIN {FS=":"} {print $1 "\'s home is " $6}' /etc/passwd
3. 模式 (Patterns)
模式决定了哪些行会触发动作。它非常灵活。
- 空模式:没有模式,则动作作用于每一行。
awk '{print $0}' # 打印每一行 - 正则表达式模式:使用
/regex/来匹配。# 打印包含 "error" 或 "Error" 的行 awk '/[Ee]rror/ {print}' logfile.txt - 行范围模式:使用
NR或FNR。# 打印第5到第10行 awk 'NR>=5 && NR<=10' file.txt - 字符串匹配:针对特定字段。
# 打印第一个字段是 "Alice" 的行 awk '$1 == "Alice"' data.txt - BEGIN/END 特殊模式:在处理任何行之前/之后执行一次。
# 计算文件总行数并输出 awk 'END {print NR}' file.txt # 添加表头 awk 'BEGIN {print "Name\tAge"} {print $1 "\t" $2} END {print "--- End of Report ---"}' data.txt - 比较表达式:使用
==,!=,>,<,>=,<=。# 打印第二个字段大于100的行 awk '$2 > 100' sales.txt - 布尔逻辑:使用
&&(与),||(或),!(非)。# 打印第一个字段是 "Apple" 且数量大于10的行 awk '$1 == "Apple" && $3 > 10' fruit.txt
三、高级功能:变量、数组与控制流
awk 是一门语言,所以它支持变量、数组、条件判断和循环。
1. 自定义变量
无需声明,直接使用。
# 计算总金额并输出
awk '{total += $2 * $3} END {print "Total Value: " total}' sales.txt
2. 条件语句 (if-else)
# 给不同销售额分级
awk '{if ($2 > 1000) grade="A"; else if ($2 > 500) grade="B"; else grade="C"; print $1, grade}' sales.txt
3. 循环 (for, while)
# 逆序打印每一行的所有字段
awk '{for (i=NF; i>0; i--) printf "%s ", $i; printf "\n"}' file.txt
4. 数组 (Array) - 极其强大的功能
awk 的数组是关联数组(类似于其他语言中的字典或 Map),索引可以是数字或字符串。
# 统计不同IP的访问次数 (假设第一列是IP)
awk '{ip_count[$1]++} END {for (ip in ip_count) print ip, ip_count[ip]}' access.log
# 计算每个部门的工资总和 (假设第二列是部门,第三列是工资)
awk '{dept_sum[$2] += $3} END {for (d in dept_sum) print d, dept_sum[d]}' employee.txt
四、内置函数
awk 提供了大量内置函数来处理字符串和算术运算。
- 字符串函数:
length(str):返回字符串长度。substr(str, start, len):提取子串。split(str, arr, sep):将字符串分割到数组。gsub(regex, repl, str):全局替换。tolower(str),toupper(str):转换大小写。
- 算术函数:
int(num):取整。rand():返回随机数。srand():设置随机数种子。
示例:
# 将第一个字段转换为大写
awk '{print toupper($1)}' file.txt
# 提取日期字符串中的年份
awk '{print substr($1, 1, 4)}' dates.txt
五、实用示例合集
-
格式化输出:
# 像表格一样对齐输出 awk '{printf "%-10s %5d %10.2f\n", $1, $2, $3}' data.txt # %-10s: 左对齐,10字符宽的字符串 # %5d: 右对齐,5字符宽的整数 # %10.2f:右对齐,10字符宽,保留2位小数 -
模拟其他命令:
# 模拟 head -n 5 awk 'NR <= 5' file.txt # 模拟 tail -n 5 (需要先读完文件,效率不高) awk '{lines[NR]=$0} END {for(i=NR-4;i<=NR;i++) print lines[i]}' file.txt # 模拟 wc -l awk 'END {print NR}' file.txt # 模拟 grep "pattern" awk '/pattern/' file.txt -
复杂数据处理:
# 找出重复的行 awk '{a[$0]++} END {for (line in a) if (a[line] > 1) print line}' file.txt # 连接两个文件 (类似SQL JOIN),基于第一个字段 # 先读取第一个文件到数组,再处理第二个文件 awk 'NR==FNR {user[$1]=$2; next} $1 in user {print $1, user[$1], $3}' file1.txt file2.txt # `NR==FNR` 仅在处理第一个文件时为真 # `next` 跳过后续动作,直接读下一行
总结
awk 是一个极其强大的文本处理工具,其精髓在于:
pattern {action}是核心模型。- 熟练使用内置变量(
FS,OFS,NR,NF,$0,$1…)。 - BEGIN 和 END 块用于初始化和收尾工作。
- 关联数组是实现统计、去重、JOIN等复杂操作的杀手锏。
- 结合正则表达式、条件判断和循环,可以解决几乎所有的文本处理问题。
对于系统管理员、开发者和数据分析师来说,花时间深入学习和掌握 awk 是一项回报率极高的投资。它能让很多原本需要编写 Python/Perl 脚本的任务,在命令行中一行搞定。
2329

被折叠的 条评论
为什么被折叠?



