AI Infra 训练营
总览
  • Day 1 · 集群起步 + CNI
  • Day 2 · 控制面 + etcd
  • Day 3 · CRD + Operator + Webhook
  • Day 4 · 存储深度
  • Day 5 · 卷扩容 + 安全
  • Day 6 · 调度 + 可观测
  • Day 7 · Harbor + ArgoCD + Mesh
  • Day 8 · AI Infra
  • Day 9 · Triton + GPU
  • Day 10 · MIG + HPA + 量化
  • Day 11 · AI Agent 端到端
  • Day 12 · 灾备
  • Day 13 · Operator + 联邦 + Mesh + RAG
  • Day 14 · CKA / CKS + 总结
  • LLM 训练手册
  • RAG + Agent 手册
  • 推理优化手册
  • 上下文工程手册
  • Agent 开发手册
  • 面试深度复盘
  • 训练 v2 深度手册
  • 心智模型
  • 看懂命令输出
  • 容器网络底层
  • K8s 网络深入
  • DNS 全套
  • 故障排查方法论
  • 心智模型
  • 容器挂载完整指南
  • K8s Volumes 大全
  • PV/PVC/CSI 深入
  • NFS 深入
  • 分布式存储概览
  • 故障排查 runbook
HiHuo 主站
GitHub
总览
  • Day 1 · 集群起步 + CNI
  • Day 2 · 控制面 + etcd
  • Day 3 · CRD + Operator + Webhook
  • Day 4 · 存储深度
  • Day 5 · 卷扩容 + 安全
  • Day 6 · 调度 + 可观测
  • Day 7 · Harbor + ArgoCD + Mesh
  • Day 8 · AI Infra
  • Day 9 · Triton + GPU
  • Day 10 · MIG + HPA + 量化
  • Day 11 · AI Agent 端到端
  • Day 12 · 灾备
  • Day 13 · Operator + 联邦 + Mesh + RAG
  • Day 14 · CKA / CKS + 总结
  • LLM 训练手册
  • RAG + Agent 手册
  • 推理优化手册
  • 上下文工程手册
  • Agent 开发手册
  • 面试深度复盘
  • 训练 v2 深度手册
  • 心智模型
  • 看懂命令输出
  • 容器网络底层
  • K8s 网络深入
  • DNS 全套
  • 故障排查方法论
  • 心智模型
  • 容器挂载完整指南
  • K8s Volumes 大全
  • PV/PVC/CSI 深入
  • NFS 深入
  • 分布式存储概览
  • 故障排查 runbook
HiHuo 主站
GitHub

awk —— 按字段处理文本

一句话定义

awk 是个小型脚本语言,但 99% 的运维场景只用它的一个特性:把每行按空白拆成字段,操作字段。

ps aux | awk '{print $2, $11}'           # 打 PID 和命令
df -h | awk '$5+0 > 80 {print $0}'        # 用量超 80% 的文件系统
echo "a b c" | awk '{print $2}'           # 输出 "b"

记住一句话:$1 $2 $3 是字段,$0 是整行,NF 是字段数。

典型场景

  • 提取列:kubectl get pods | awk '{print $1}'
  • 按列过滤:df -h | awk '$5+0 > 80'
  • 求和 / 计数:awk '{sum+=$3} END {print sum}'
  • 改 CSV:awk -F, '{...}'
  • 配合 sed/grep 后的最后一道处理

三种使用形态

1. 单行内嵌(99% 用法)

ps aux | awk '{print $2}'

{...} 里写一段 awk 代码,对每行执行。

2. 带模式

awk '/pattern/ {action}' file
ps aux | awk '/nginx/ {print $2}'         # 含 "nginx" 的行打 PID

3. 写到文件

awk -f script.awk input

日常很少这么用——能写到文件的逻辑早该用 Python 了。


awk 的执行模型(最小够用版)

        ┌──────────┐
input ─►│  BEGIN { }  │      (所有行之前跑一次)
        ├──────────┤
        │  pattern { action }   │      (对每行:匹配 pattern → 跑 action)
        │  pattern { action }   │
        │  ...                   │
        ├──────────┤
        │  END { }              │      (所有行之后跑一次)
        └──────────┘

例子:

ls -l | awk 'BEGIN {sum=0} {sum+=$5} END {print "total:", sum}'
# 算所有文件总字节数

字段:$1 $2 ... $0

echo "a b c d" | awk '{print $1}'         # a
echo "a b c d" | awk '{print $3}'         # c
echo "a b c d" | awk '{print $0}'         # a b c d  整行
echo "a b c d" | awk '{print NF}'         # 4  字段数
echo "a b c d" | awk '{print $NF}'        # d  最后一个字段
echo "a b c d" | awk '{print $(NF-1)}'    # c  倒数第二

$NF 是日常救命:取最后一列,不需要数到底有多少列。


分隔符 -F

默认按任意连续空白分字段(空格、tab)。要改:

# CSV
awk -F, '{print $2}' data.csv

# 冒号分隔(/etc/passwd)
awk -F: '{print $1}' /etc/passwd          # 列出所有用户名

# 多字符分隔
awk -F'::' '{print $2}' weird.log

# 正则分隔
awk -F'[ ,;]+' '{print $1, $3}' messy.log

或者在 BEGIN 里设:

awk 'BEGIN{FS=":"} {print $1}' /etc/passwd

-F 和 BEGIN{FS=":"} 等价。-F 更简短。

输出分隔符 OFS

echo "a b c" | awk 'BEGIN{OFS="|"} {print $1,$2,$3}'
# a|b|c

OFS(output field separator)控制 print 用逗号隔开的字段在输出时拿什么连接。默认是空格。


模式(匹配什么行)

awk '/pattern/' file                      # 等价 grep,打印匹配行
awk '!/pattern/' file                     # 等价 grep -v
awk '/start/,/end/' file                  # 范围(grep 干不了)

# 按字段判断
awk '$3 == "running"' file                # 第三列等于 running
awk '$3 != "running"' file
awk '$5+0 > 80'                           # 第五列数字大于 80
awk 'NF > 0' file                         # 非空行
awk 'NR > 1' file                         # 跳过第一行(标题)
awk 'NR == FNR' file1 file2               # 处理 file1 时(NR == FNR)

NR = number of record(当前行号),FNR = current file's NR,NF = number of fields。

$5+0 强转数字:awk 字段默认是字符串,比较大小要 +0 转数字(或参与算术运算)。


最实用的 10 个例子

1. 取列

ps aux | awk '{print $2}'                          # PID
ps aux | awk '{print $11}'                         # 命令
kubectl get pods | awk 'NR>1 {print $1}'           # pod 名(跳过表头)
df -h | awk 'NR>1 {print $1, $5}'                  # 文件系统 + 用量

2. 求和 / 计数

# 总字节数
ls -l | awk '{sum+=$5} END {print sum}'

# 计数
ps aux | awk '/nginx/ {count++} END {print count}'

# 行数(等价 wc -l)
awk 'END {print NR}' file

3. 平均

awk '{sum+=$3; count++} END {print sum/count}' file

4. 找最大 / 最小

awk '$3 > max {max=$3; line=$0} END {print line}' file

5. 去重统计(uniq -c 的强化版)

awk '{count[$1]++} END {for (k in count) print count[k], k}' file | sort -rn

count[$1]++ —— 关联数组(hash map)。awk 内置 hash 是它最强的功能之一。

6. 修改某列

# 给第二列加引号
echo "a 1 x" | awk '{print $1, "\""$2"\"", $3}'

# 给所有数字加 1
echo "1 2 3" | awk '{print $1+1, $2+1, $3+1}'

7. 多条件

awk '$3 == "Running" && $5 ~ /h$/' file
# 第 3 列是 Running 且第 5 列以 h 结尾

~ 是正则匹配,!~ 是不匹配。

8. 条件分支

df -h | awk 'NR>1 {if ($5+0 > 80) print "WARN:", $0; else print "OK:", $1}'

9. 跨行求和(按 key 分组)

# CSV:name, value
awk -F, '{sum[$1]+=$2} END {for (k in sum) print k, sum[k]}' data.csv

10. 第一列改大写

awk '{print toupper($1), $2, $3}' file

toupper() / tolower() / length() / substr(s, start, len) 是常用内置函数。


训练营典型用法

看哪些 pod 不是 Running

kubectl get pods -A | awk '$4 != "Running" && NR > 1'

统计每个节点的 pod 数

kubectl get pods -A -o wide | awk 'NR>1 {count[$8]++} END {for (n in count) print n, count[n]}'

看 /var/log/auth.log 里登录失败的 IP

grep "Failed password" /var/log/auth.log | awk '{print $(NF-3)}' | sort | uniq -c | sort -rn

df 报告超 80% 的分区

df -h | awk 'NR>1 && $5+0 > 80 {print $0}'

journalctl 找最常出现的错误模板

journalctl -u kubelet --since today -o cat \
  | awk '{print $1, $2, $3, $4, $5}' \
  | sort | uniq -c | sort -rn | head

awk 自带功能速查

内置变量

变量含义
$0整行
$1..$NF字段
NF当前行字段数
NR当前行号(全局)
FNR当前文件内行号
FS输入分隔符(默认空白)
OFS输出分隔符(默认空格)
RS输入记录分隔符(默认换行)
ORS输出记录分隔符
FILENAME当前文件名

内置函数

函数用途
length(s)字符串长度
substr(s, start, len)子串
index(s, sub)找子串位置
split(s, arr, sep)按 sep 拆 s 到数组 arr
toupper(s) / tolower(s)大小写
gsub(re, rep, target)全局替换
sub(re, rep, target)单次替换
match(s, re)正则匹配
sprintf(fmt, args...)格式化字符串(仿 C)
printf("...", args)格式化输出
int(x)转整数

控制流

if (...) {...} else {...}
for (i=0; i<10; i++) {...}
for (k in arr) {...}
while (...) {...}
break / continue

awk 是个真·语言,但日常 1-2 行就够。写到三行以上就该用 Python 了。


常见踩坑

坑 1:字段索引错乱

ps aux 的输出格式:

USER  PID  %CPU  %MEM  VSZ  RSS  TTY  STAT  START  TIME  COMMAND
root  1234 0.5   1.2   ...  ...  ?    Ss    May27  0:00  /usr/sbin/sshd -D
                                                          ───────────
                                                            $11 起

$11 是命令的第一个 token;如果命令有空格,需要 $11, $12, ... 拼接。要拿整个命令:

ps aux | awk '{for(i=11;i<=NF;i++) printf "%s ", $i; print ""}'

或者更现代的写法用 pgrep -a、ps -eo cmd:

ps -eo pid,cmd

坑 2:字符串和数字混淆

echo "5" | awk '{if ($1 > "10") print "big"}'        # awk 比较字符串:"5" > "10" 字典序 → big
echo "5" | awk '{if ($1+0 > 10) print "big"}'        # 数字比较 → 不打印

字段都是字符串。要数字比较:永远 $N+0 或者参与算术运算。

坑 3:-F 是字符还是正则

awk -F. '{print $1}' file       # 按 . 分?看 awk 实现

GNU awk(gawk)的 -F 默认是字符字面量。但单字符默认是字符,多字符是正则。最稳的写法:

awk -F'[.]' '{print $1}' file

坑 4:单引号里的引号

awk '{print "PID: " $2}'        # 双引号在单引号外面 OK
awk '{print '"'"'PID: '"'"' $2}'  # 这种嵌套很恶心

遇到要嵌引号,写脚本文件 -f script.awk,或者用 Python。

坑 5:用 awk 替代复杂逻辑

awk 能干很多事,但一旦超过 2-3 行就该停下问"这是 Python 干的活吗"。awk 没好的错误处理、没好的调试器、没结构化输出。


关联命令

  • grep —— 找行用 grep
  • sed —— 简单替换用 sed
  • sort / uniq —— 经常和 awk 串联
  • cut —— 简单的"按列取",比 awk 更轻:cut -d: -f1 /etc/passwd
  • column —— 把 awk 输出对齐显示
  • jq —— JSON 用 jq,不要用 awk 解析 JSON
在 GitHub 上编辑此页