awk 命令的基本使用方法
2025/12/21大约 7 分钟
awk 命令的基本使用方法
核心概念
awk 是一个强大的文本处理工具,用来扫描和处理文本文件中的模式和数据。它的工作原理是按行读取文件,根据指定的规则进行匹配和处理,支持正则表达式、内置函数和变量。
基本语法:
awk [options] 'pattern { action }' file工作原理和基本结构
awk 的三个阶段:
1. BEGIN 块(初始化):在处理任何输入之前执行
2. 主体块(处理数据):对每一行执行
3. END 块(收尾):在处理完所有输入后执行
awk '
BEGIN { print "开始处理" } # 初始化
{ print $1, $2 } # 处理每一行
END { print "处理完成" } # 收尾
' file.txt字段分割:
- awk 默认以空格或制表符分割每行为多个字段
$0:整行内容$1, $2, $3...:第 1、2、3... 个字段NF:字段总数NR:行号
示例:
# 处理文件:name age city
# John 30 Beijing
# Jane 25 Shanghai
# 打印第一个字段(名字)
awk '{ print $1 }' file.txt
# 输出:John Jane
# 打印所有字段数
awk '{ print NF, NR }' file.txt
# 输出:3 1 和 3 2
# 自定义分割符(冒号)
awk -F: '{ print $1 }' /etc/passwd常用选项和变量
命令行选项:
# -F:指定字段分割符
awk -F: '{ print $1 }' /etc/passwd
# -v:定义变量
awk -v OFS="|" '{ print $1, $2 }' file.txt
# 使用竖线 | 作为输出分割符
# -f:从文件读取 awk 程序
awk -f script.awk file.txt内置变量:
| 变量 | 说明 | 示例 |
|---|---|---|
| NR | 当前行号 | NR > 5 处理第 5 行之后 |
| NF | 字段数 | NF >= 3 至少 3 个字段 |
| FS | 输入分割符(默认空格) | FS=":" |
| OFS | 输出分割符(默认空格) | OFS="," |
| ORS | 输出行分割符(默认换行) | ORS="\n" |
| FILENAME | 当前文件名 | print FILENAME, $0 |
| FNR | 当前文件内的行号 | 处理多文件时区分 |
示例:
# 打印行号和内容
awk '{ print NR ":", $0 }' file.txt
# 处理 /etc/passwd,以冒号分割
awk -F: '{ print $1 "\t" $3 }' /etc/passwd
# 输出:用户名和 UID
# 计数字段
awk '{ sum += $1 } END { print sum }' numbers.txt模式和条件
两种模式类型:
1. 正则表达式模式:
# 匹配包含 "error" 的行
awk '/error/ { print }' logfile.txt
# 忽略大小写匹配
awk 'tolower($0) ~ /error/' logfile.txt
# 不匹配模式
awk '!/error/ { print }' logfile.txt2. 条件模式:
# 打印工资大于 5000 的员工
awk '$3 > 5000 { print $1, $3 }' salary.txt
# 多条件
awk '$2 > 25 && $3 == "Beijing" { print }' data.txt
# 第一行到第五行
awk 'NR >= 1 && NR <= 5' file.txt
# 或简写
awk 'NR==1, NR==5' file.txt # 范围模式3. 范围模式:
# 从包含 "start" 的行到包含 "end" 的行
awk '/start/, /end/ { print }' file.txt常用操作和函数
字符串和数学函数:
# 长度
awk '{ print length($0) }' file.txt
# 子串
awk '{ print substr($1, 1, 3) }' file.txt # 从第 1 位取 3 个字符
# 查找位置
awk '{ print index($1, "abc") }' file.txt # 找 "abc" 的位置
# 替换
awk '{ print gsub(/old/, "new") }' file.txt # 替换所有
awk '{ print sub(/old/, "new") }' file.txt # 替换第一个
# 转大小写
awk '{ print toupper($1), tolower($2) }' file.txt
# 数学函数
awk '{ print sqrt($1), int($2) }' file.txt分组统计:
# 统计不同城市的人数
awk '{ count[$3]++ } END { for (city in count) print city, count[city] }' data.txt
# 计算平均工资
awk '{ sum += $2; n++ } END { print sum/n }' salary.txt
# 按字段分组求和
awk '{ total[$1] += $2 } END { for (name in total) print name, total[name] }' sales.txt输出和格式化:
# printf 格式化输出
awk '{ printf "%s\t%d\t%.2f\n", $1, $2, $3 }' file.txt
# %s 字符串,%d 整数,%.2f 两位小数
# 条件输出
awk '{ if ($2 > 5000) print $1, "高薪"; else print $1, "普通" }' salary.txt常见用法示例
统计和聚合:
# 统计行数(比 wc -l 快)
awk 'END { print NR }' file.txt
# 统计词频
awk '{ for (i=1; i<=NF; i++) count[$i]++ } END { for (w in count) print w, count[w] }' file.txt
# 列求和
awk '{ sum += $1 } END { print sum }' numbers.txt
# 列求平均
awk '{ sum += $1; n++ } END { print sum/n }' numbers.txt
# 列求最大最小值
awk '{ if (NR==1) { max=$1; min=$1 } else { if ($1>max) max=$1; if ($1<min) min=$1 } } END { print "Max:", max, "Min:", min }' numbers.txt数据提取和转换:
# 提取特定列(类似 cut)
awk '{ print $2, $4 }' file.txt
# 去重(第一次出现的行)
awk '!seen[$0]++' file.txt
# 或
awk '!a[$0]++' file.txt
# 反转列顺序
awk '{ for (i=NF; i>=1; i--) print $i }' file.txt
# 列转行
awk '{ for (i=1; i<=NF; i++) print $i }' file.txt日志和文本分析:
# 统计 HTTP 状态码
awk '{ count[$9]++ } END { for (code in count) print code, count[code] }' access.log
# 统计 IP 访问次数
awk '{ count[$1]++ } END { for (ip in count) print ip, count[ip] }' access.log
# 查找并计数错误日志
awk '/ERROR/ { error_count++ } END { print "Errors:", error_count }' app.log
# 统计各进程占用内存
ps aux | awk '{ memory[$11] += $6 } END { for (proc in memory) print proc, memory[proc] }'数据清洗和格式转换:
# 删除重复空行
awk 'NF { print; p=1; next } p' file.txt
# 去掉开头和末尾空格
awk '{ gsub(/^[ \t]+|[ \t]+$/, ""); print }' file.txt
# 在特定行前后添加文本
awk '/pattern/ { print "prefix"; print $0; print "suffix"; next } { print }' file.txt
# CSV 转 TSV
awk -F, '{ OFS="\t"; print $1, $2, $3 }' file.csv一句话 awk 示例汇总
# 1. 打印特定列
awk '{ print $1, $3 }' file.txt
# 2. 条件过滤
awk '$2 > 100 { print }' file.txt
# 3. 统计行数
awk 'END { print NR }' file.txt
# 4. 字段求和
awk '{ sum += $1 } END { print sum }' file.txt
# 5. 去重
awk '!a[$0]++' file.txt
# 6. 反转字段顺序
awk '{ for (i=NF; i>=1; i--) printf "%s ", $i; print "" }' file.txt
# 7. 按字段分组统计
awk '{ sum[$1] += $2 } END { for (k in sum) print k, sum[k] }' file.txt
# 8. 替换文本
awk '{ gsub(/old/, "new"); print }' file.txt
# 9. 格式化输出
awk '{ printf "%-10s %5d\n", $1, $2 }' file.txt
# 10. 计算平均值
awk '{ sum += $1; n++ } END { print sum/n }' file.txt相关高频面试题
Q1: 如何用 awk 提取日志文件中的特定字段和统计数据?
答案:
# 示例日志格式:
# 192.168.1.1 - - [20/Dec/2024:10:00:00] "GET /index.html HTTP/1.1" 200 1234
# 1. 提取 IP 地址和状态码
awk '{ print $1, $9 }' access.log
# 2. 统计不同状态码的请求数
awk '{ count[$9]++ } END { for (code in count) print code, count[code] }' access.log
# 3. 统计各 IP 的请求次数(Top 10)
awk '{ count[$1]++ } END { for (ip in count) print ip, count[ip] }' access.log | sort -k2 -rn | head -10
# 4. 统计流量最高的 URL
awk '{ sum[$7] += $10 } END { for (url in sum) print url, sum[url] }' access.log | sort -k2 -rn | head -10
# 5. 统计特定时间段的请求
awk '$4 >= "[20/Dec/2024:10:00:00" && $4 < "[20/Dec/2024:11:00:00" { count++ } END { print count }' access.logQ2: awk 中的数组有什么特点?如何使用关联数组?
答案:
# awk 的数组都是关联数组(类似哈希表),不是索引数组
# 键可以是任意字符串,不仅仅是数字
# 1. 计数
awk '{ count[$1]++ } END { for (key in count) print key, count[key] }' file.txt
# 2. 求和
awk '{ sum[$1] += $2 } END { for (key in sum) print key, sum[key] }' file.txt
# 3. 判断是否存在
awk '{ if ($1 in dict) print "exists"; else print "not exists" }' file.txt
# 4. 删除数组元素
awk '{ if (count[$1] > 1) delete count[$1] } END { for (k in count) print k }' file.txt
# 5. 二维数组
awk '{ arr[$1, $2]++ } END { for (k in arr) print k, arr[k] }' file.txt
# 访问:arr[key1, key2] 或 arr[key1 SUBSEP key2]Q3: awk 的 gsub 和 sub 函数有什么区别?
答案:
# sub:替换第一个匹配
awk '{ sub(/old/, "new"); print }' file.txt
# "old old old" → "new old old"
# gsub:替换所有匹配
awk '{ gsub(/old/, "new"); print }' file.txt
# "old old old" → "new new new"
# 修改特定字段
awk '{ gsub(/[0-9]/, "X", $2); print }' file.txt # 只修改第 2 个字段
# 返回值是替换次数
awk '{ n = gsub(/old/, "new"); print n, $0 }' file.txtQ4: awk 中的 split 函数有什么用?
答案:
# 将字符串按分割符分割成数组
# 1. 基本用法
awk '{ n = split($0, arr, ":"); for (i=1; i<=n; i++) print arr[i] }' file.txt
# 2. 动态处理字段
echo "a:b:c" | awk '{ split($0, a, ":"); print a[1], a[3] }'
# 输出:a c
# 3. 统计分割后的数据
awk '{ split($0, parts, ","); sum += parts[2] } END { print sum }' file.csv
# 4. 处理嵌套数据
awk '{ split($0, a, "|"); split(a[1], b, ":"); print b[2] }' file.txtQ5: awk 的 printf 如何格式化输出?
答案:
# printf 的常用格式符
# %d 整数,%f 浮点,%s 字符串,%x 十六进制
# 修饰符:- 左对齐,0 补零,. 精度
# 1. 基本格式
awk '{ printf "%s %d %.2f\n", $1, $2, $3 }' file.txt
# 2. 宽度和对齐
awk '{ printf "%-15s %5d %10.2f\n", $1, $2, $3 }' file.txt
# %-15s:左对齐 15 个字符
# %5d:右对齐 5 位整数
# %10.2f:10 位宽,2 位小数
# 3. 补零
awk '{ printf "%05d\n", $1 }' file.txt
# 12 → 00012
# 4. 百分比格式
awk '{ printf "%.1f%%\n", $1 * 100 }' file.txtQ6: awk 如何处理多文件?FNR 和 NR 的区别?
答案:
# NR:总行号(所有文件)
# FNR:当前文件的行号(会重置)
# 示例:处理两个文件
awk '{ print FILENAME, FNR, NR, $0 }' file1.txt file2.txt
# 输出可能为:
# file1.txt 1 1 ...
# file1.txt 2 2 ...
# file2.txt 1 3 ... ← FNR 重置,NR 继续增长
# file2.txt 2 4 ...
# 处理多文件的合并操作
awk 'FNR == 1 { print "=== " FILENAME " ===" } { print }' file1.txt file2.txt
# 只处理第一个文件
awk 'FNR == NR { print }' file1.txt file2.txt
# 或
awk 'FILENAME == "file1.txt" { print }' file1.txt file2.txt快速参考
常用组合:
# 统计字符频率
awk '{ for (i=1; i<=length($0); i++) count[substr($0,i,1)]++ }
END { for (c in count) print c, count[c] }' file.txt
# 打印第 N 到 M 行
awk 'NR >= 5 && NR <= 10' file.txt
# 按字段排序(使用外部工具)
awk '{ print }' file.txt | sort -k2 -n
# 统计文件大小最大的前 3 个
ls -l | awk '{ if (NR > 1) print $9, $5 }' | sort -k2 -rn | head -3