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

grep —— 文本搜索

一句话定义

grep 在文本里按模式找匹配的行。模式默认是基础正则(BRE),可以用 -E 切扩展正则、-F 切字面量、-P 切 PCRE(GNU grep)。

典型场景

grep 是 shell 工具链里出场频次最高的命令之一(训练营文档里 40+ 次)。最常见的用法只有几个:

grep "ERROR" app.log                            # 在文件里找
grep -i "error" app.log                         # 不分大小写
grep -r "TODO" src/                             # 递归找目录
ps aux | grep nginx                             # 配合管道
journalctl -u kubelet --since today | grep -i 'oom\|killed'

但 grep 有一组 flag 容易让人困惑,特别是 -F、-x、-q、-w,下面专门讲清楚。


五种模式语法

Flag模式语言何时用
默认 / -GBRE(基础正则). * ^ $ [] 是元字符;+ ? | ( ) 要转义简单匹配
-EERE(扩展正则)+ ? | ( ) 直接生效推荐默认
-F固定字符串(字面量),不当正则解析模式里含 + . / * ? 等正则字符时
-PPCRE(Perl 兼容正则),支持 \d \w lookahead 等复杂正则
-x整行匹配(-x 不是另一种语法,是个限定符)严格匹配整行

推荐:除非有特殊原因,平时用 grep -E 而不是默认的 BRE。 BRE 那种 \+ \? 反斜线转义是历史包袱、容易写错。

-F 的用处:处理"模式里有正则元字符"的情况

grep "a.b" file        # ❌ 默认 BRE,`.` 是任意字符,会匹配 "aab", "axb" 等
grep -F "a.b" file     # ✅ 字面量,只匹配 "a.b"

ssh-keygen.md 提到的"推 key 幂等"用 grep -qxF:

grep -qxF "$PUBKEY" /root/.ssh/authorized_keys 2>/dev/null \
  || echo "$PUBKEY" >> /root/.ssh/authorized_keys

为什么要 -F:公钥里有 + / = 等正则元字符,不加 -F 会被 grep 当 regex 解析、结果完全不对。


最常用的 8 个 flag

grep -i "error" log         # ignore case
grep -v "DEBUG" log         # invert:不匹配的行
grep -n "TODO" src/         # 加行号
grep -c "ERROR" log         # 统计匹配的行数
grep -l "TODO" *.md         # 只输出包含匹配的文件名
grep -L "License" *.md      # 只输出不包含匹配的文件名(反向)
grep -r "TODO" src/         # 递归
grep -A 3 "ERROR" log       # 匹配行后 3 行上下文
grep -B 3 "ERROR" log       # 匹配行前 3 行上下文
grep -C 3 "ERROR" log       # 前后各 3 行

-A / -B / -C 是排错神器:

journalctl -u kubelet | grep -B 2 -A 5 "fatal"
# 看 fatal 错误前后的上下文(前 2 行 + fatal + 后 5 行)

-q 静默模式(脚本最爱)

grep -q "pattern" file && echo "matched" || echo "not matched"
  • -q 不输出,只用退出码:找到 → 0,没找到 → 1
  • 找到第一行就返回,不读完整文件(大文件提速)

经典用法:

if grep -q "^PasswordAuthentication" /etc/ssh/sshd_config; then
  sed -i 's/^PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
else
  echo "PasswordAuthentication no" >> /etc/ssh/sshd_config
fi

-x 整行匹配

echo "hello world" | grep -x "hello"       # 不匹配(不是整行)
echo "hello" | grep -x "hello"             # 匹配

-x 让模式必须从行首到行尾完全等于。和 ^pattern$ 一回事,但更短。

公钥幂等场景 -x 是关键:

PUBKEY="ssh-ed25519 AAAA... user@laptop"
echo "$PUBKEY" >> /root/.ssh/authorized_keys           # 加进去

# 第二次跑,不希望重复加
grep -qxF "$PUBKEY" /root/.ssh/authorized_keys \
  || echo "$PUBKEY" >> /root/.ssh/authorized_keys

为什么必须 -x:如果不加,假设你的公钥是 "AAAA"、authorized_keys 里有别人公钥是 "AAAAB",子串匹配会误判已存在 → 漏加你的 key。

三个加在一起:grep -qxF

  • -q 静默
  • -x 整行
  • -F 字面量

幂等追加配置行的标准套路:

grep -qxF "fs.inotify.max_user_watches = 524288" /etc/sysctl.conf \
  || echo "fs.inotify.max_user_watches = 524288" >> /etc/sysctl.conf

-w 整词匹配

echo "foo foobar" | grep "foo"        # 匹配(含 foobar)
echo "foo foobar" | grep -w "foo"     # 还是匹配,但只在 "foo" 这个词上

-w 让 grep 只在词边界(非字母数字下划线 → 字母数字下划线)匹配。常用:

grep -w "kubelet" /etc/passwd                      # 不会匹配到 "kubeletx"
ps aux | grep -w "[s]sh"                           # 加 [s] 防匹配 grep 自己

递归 -r / -R

grep -r "TODO" src/                    # 递归(跟随软链)
grep -R "TODO" src/                    # 同上(GNU 里两者一致)
grep -rn "TODO" src/                   # 加行号
grep -rln "TODO" src/                  # 只列文件名 + 行号
grep -r --include="*.md" "TODO" .      # 只搜 .md
grep -r --exclude-dir=node_modules "TODO" .   # 排除目录

重大限制:grep -r 不读 .gitignore,会搜进 node_modules / vendor / .git/,慢且噪音多。

更好的替代:

  • rg (ripgrep):默认遵守 .gitignore,速度比 grep 快几倍
  • grep -r --exclude-dir={node_modules,.git,vendor} 折中

常见模式速查

grep "^ERROR" log              # 行首
grep "fatal$" log              # 行尾
grep "^$" log                  # 空行
grep -v "^$" log               # 排除空行
grep "^#" config               # 注释行
grep -v "^#" config            # 排除注释行
grep -v "^#" config | grep -v "^$"    # 排除注释 + 空行(看实际生效配置)
grep "[0-9]\{3\}" log          # BRE 反斜线
grep -E "[0-9]{3}" log         # ERE 不用转义(更清爽)

清理配置文件套路:

grep -v "^#" /etc/ssh/sshd_config | grep -v "^$"
# 干净地看实际生效配置(不算 sshd_config.d 里的)

多模式 -e 或 egrep | 操作

grep "ERROR" log | grep "DB"           # 两步:先错、再 DB

grep -e "ERROR" -e "WARNING" log       # OR:匹配任一
grep -E "ERROR|WARNING" log            # 等价 OR

# 想 AND:用 awk 或两次 grep
grep "ERROR" log | grep "DB"           # AND

grep 不支持原生 AND。要 AND:管道 / awk / grep -P lookahead。


颜色与可读性

grep --color=auto "ERROR" log          # 匹配处高亮
grep --color=always "ERROR" log | less -R    # 通过 less 也保留颜色

通常 .bashrc 里设:

alias grep='grep --color=auto'

实战例子

装机审计:找 IDC 塞的 agent

systemctl list-unit-files --state=enabled | grep -iE 'agent|aliyun|aegis|nezha|monitor|cloud'

K8s 节点排错

# kubelet 最近的错误
journalctl -u kubelet --since today | grep -i "error\|fail" | tail -20

# 找 OOM
journalctl -k -b | grep -iE 'oom|killed process'

# 看 sshd 谁登过
journalctl -u sshd --since yesterday | grep -i "accepted\|failed"

配置文件的实际生效行

grep -vE '^\s*(#|$)' /etc/sysctl.conf      # 排除注释和空行

-E + ^\s*(#|$) —— 行首可能有空格、然后 # 或行尾。

找 K8s YAML 里某个字段

grep -rn "imagePullPolicy" manifests/
grep -rn "namespace:" manifests/

查二进制文件里的字符串

grep -a "VERSION" /usr/bin/kubectl       # -a 当作文本处理(默认二进制会被跳过)

通常更好用 strings:

strings /usr/bin/kubectl | grep -i version

常见踩坑

坑 1:grep nginx 把自己匹配进去了

$ ps aux | grep nginx
root     1234  ...  nginx: master
root     1235  ...  nginx: worker
root     5678  ...  grep --color=auto nginx       ← grep 自己

经典 hack:把模式第一个字符放进字符类:

ps aux | grep "[n]ginx"        # 模式是 [n]ginx 实际匹配 nginx
                                # 但 ps 里 grep 进程的命令行是 grep [n]ginx,不匹配自己

或者用 pgrep:

pgrep nginx                    # 只显示 PID
pgrep -a nginx                 # 显示完整命令行

坑 2:grep 慢的离谱

grep -r "TODO" /                       # 搜根目录,可能跑几小时

-r 不读 .gitignore。代码仓库里跑:

  • --exclude-dir={node_modules,.git,vendor,dist}
  • 或者用 rg(更快、默认排除)

坑 3:BRE 和 ERE 转义混乱

grep "(foo|bar)" log           # BRE:把 (...|...) 当字面量 → 多半不匹配
grep -E "(foo|bar)" log        # ERE:正确
grep "\(foo\|bar\)" log        # BRE 转义:等价 ERE 第二个

统一用 -E,少踩坑。

坑 4:grep 在管道里不刷新

journalctl -u kubelet -f | grep "ERROR"
# 实时输出?不,缓冲在 grep 里、可能延迟几秒

grep 默认在管道里用块缓冲(4K)。用 --line-buffered:

journalctl -u kubelet -f | grep --line-buffered "ERROR"

或者 stdbuf:

journalctl -u kubelet -f | stdbuf -oL grep "ERROR" | tee out.log

坑 5:编码 / 字节序问题

文件含 UTF-8 BOM 或者奇怪编码时 grep 可能找不到。file <path> 看编码、iconv 转换。

坑 6:grep -v 用于"找不出某行"逻辑反了

grep -v "ERROR" log | head            # 输出"不含 ERROR 的行",几乎全部输出

-v 是反向匹配,不是"找不到 ERROR 就报错"。要后者:

grep -q "ERROR" log || echo "no errors"

坑 7:grep "$" / "*" 这种空字符串模式

grep "$VAR" file               # 如果 VAR 为空,等价 grep "" file,匹配所有行

变量为空时记得加默认值:grep "${VAR:-NONE_MATCH}"。


关联命令

  • rg (ripgrep) —— 现代替代,更快、默认遵守 gitignore
  • ack / ag —— 老的"代码 grep"工具
  • sed —— grep 找到之后修改用 sed
  • awk —— 要按字段处理用 awk
  • find —— 按文件元信息找、然后 -exec grep
  • xargs —— 把 grep -l 的输出喂给下一个命令
在 GitHub 上编辑此页