排查 Nginx 日志 404
凌晨 1 点,运维收到告警,某 API 接口 5 分钟内返回了 1200 个 404。他 ssh 上服务器,`grep '404' access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -5`,一行命令就找到被攻击的路径是 `/api/old/order`。之前他得把日志拖到本地用 Excel 筛,现在 30 秒定位到问题,直接回滚了那条被误删的路由规则。
管道符断了,grep 卡在不动,awk 脚本报错——不是命令不会写,是参数记混了。这个页面把 find、grep、awk、sed 的常用参数按场景排好,每条附一行实测例子,鼠标悬停就能看效果。所有解析在浏览器内完成,命令和结果都不出本地,适合写脚本时查参数、调管道时对语法。
凌晨 1 点,运维收到告警,某 API 接口 5 分钟内返回了 1200 个 404。他 ssh 上服务器,`grep '404' access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -5`,一行命令就找到被攻击的路径是 `/api/old/order`。之前他得把日志拖到本地用 Excel 筛,现在 30 秒定位到问题,直接回滚了那条被误删的路由规则。
DBA 发现数据库 CPU 飙到 90%,`mysqldumpslow` 输出太粗。他用 `grep 'Query_time: [0-9]\+' slow.log | awk '{print $3}' | sort -nr | head -20` 提取出最慢的 20 条查询时间,再 `sed -n '/# Query_time: 12\./,/^#/p' slow.log` 切出那条耗时 12.3 秒的 SQL 全文。发现是 JOIN 没走索引,直接加索引解决问题,不用重启数据库。
上线前要核对 3 台生产服务器的 nginx.conf 是否一致。以前靠肉眼比对,经常漏掉一行 `keepalive_timeout` 的差异。现在 `grep -v '^\s*#' /etc/nginx/nginx.conf | sed '/^$/d' > conf_clean.txt` 去掉注释和空行,再 `diff <(cat server1.conf) <(cat server2.conf)` 直接输出差异行。发现 server2 的 `worker_connections` 写成了 512 而不是 1024,改完才敢切流量。
CI 跑了一周,`docker images` 输出 300 多行,占满 50GB 磁盘。他用 `docker images --format '{{.Repository}}:{{.Tag}} {{.CreatedAt}}' | grep 'days' | awk '{print $1}' | xargs docker rmi` 删掉所有创建时间超过 1 天的镜像。但发现有 3 个镜像被容器引用删不掉,又加 `grep -v 'error'` 过滤掉报错信息,最终回收了 32GB 空间,CI 不再报 `no space left on device`。
公司把 MySQL 从 5.7 迁移到 8.0,所有 Java 项目里的 `jdbc:mysql://old-host:3306` 要改成 `jdbc:mysql://new-host:3307`。30 个微服务,每个都有 3-5 个配置文件。他用 `find . -name '*.properties' -exec sed -i 's/old-host:3306/new-host:3307/g' {} \;` 一条命令遍历了 120 个文件。改完后 `grep -r 'old-host' .` 确认没有遗漏,比手动改快了 2 小时且零遗漏。
| 输入 | 输出 | 说明 |
|---|---|---|
| grep 'error' /var/log/syslog | 返回文件中所有包含 'error' 的行 | 常规:最基础的 grep 用法,匹配固定字符串,验证工具是否能正确读取文件并输出匹配行。 |
| find . -name '*.log' -type f | 返回当前目录下所有扩展名为 .log 的普通文件路径列表 | 常规:find 的典型组合,-name 和 -type 联合使用,验证通配符匹配和文件类型过滤。 |
| awk '{print $1}' data.txt | 输出 data.txt 每行的第一个字段(以空格/制表符分隔) | 常规:awk 默认按空白分割,$1 取第一列,验证字段切割基本功能。 |
| sed -n '5,10p' file.txt | 输出 file.txt 的第 5 到第 10 行内容 | 边界:-n 抑制默认输出,p 命令精确打印行范围,验证行号寻址和地址范围语法。 |
| grep -E '^[0-9]+$' /dev/null | 无输出(空) | 边界:输入文件为空,验证工具对空文件或空输入的处理——不应报错,应静默返回空。 |
| awk '{sum+=$1} END {print sum}' numbers.txt | 输出 numbers.txt 中第一列所有数值的总和(若文件为空则输出 0) | 易错:END 块仅在文件处理完毕后执行;若文件为空,sum 初始值为 0,输出 0,而非空或报错。 |
| sed 's/old/new/g' file.txt | 将 file.txt 中所有 'old' 替换为 'new' 并输出(不修改原文件) | 易错:sed 默认不修改原文件,需加 -i 才原地修改;初学者易误以为 s/// 会直接修改磁盘。 |
| find / -name '*.conf' -maxdepth 3 2>/dev/null | 返回根目录下深度不超过 3 层的所有 .conf 文件路径(忽略无权限目录的错误信息) | 易错:2>/dev/null 是 shell 重定向,非 find 参数;maxdepth 限制递归深度,避免系统卡死。 |
1.find 的 -exec 忘记加分号或加号
find . -name '*.log' -exec rm {}find . -name '*.log' -exec rm {} \;-exec 必须以空格+分号结尾(\;)或加号(+)表示批量执行。漏掉分号会报语法错误,因为 shell 把 {} 后的内容当作新命令。
2.grep 用基本正则时括号未转义
grep '(error|warning)' log.txtgrep -E '(error|warning)' log.txt 或 grep '\(error\|warning\)' log.txt默认 grep 使用基本正则(BRE),括号和竖线是字面字符,必须加反斜杠才表示分组或或。加 -E 切换到扩展正则(ERE)更直观。
3.awk 条件判断用 = 而非 ==
awk '$1 = "error" {print}' log.txtawk '$1 == "error" {print}' log.txtawk 中 = 是赋值运算符,会把 $1 改成 "error" 并返回真(非空字符串),导致所有行都被打印。== 才是相等比较。
4.sed 替换未加 g 只替换每行第一个
sed 's/foo/bar/' file.txtsed 's/foo/bar/g' file.txtsed 默认只替换每行第一个匹配。加 g(global)标志才替换行内所有匹配。想替换第 N 个可用数字,如 s/foo/bar/2。
5.find 的 -name 模式不加引号被 shell 展开
find . -name *.txtfind . -name '*.txt'不加引号时,shell 会把 *.txt 展开为当前目录下所有 .txt 文件名,导致 find 收到多个参数而非模式。引号阻止 shell 展开,交给 find 自己解释。
6.grep 的 -v 与 -l 组合理解错误
grep -v -l 'error' *.loggrep -l 'error' *.log | xargs grep -v 'warning'-l 只输出匹配文件名,-v 是反向匹配。两者组合时,-v 作用于文件内容而非文件名,结果可能为空或不符合预期。正确做法分两步:先找含 error 的文件,再在这些文件里排除 warning。
7.awk 里用 NR 和 FNR 混淆
awk 'NR>1{print}' file1 file2awk 'FNR>1{print}' file1 file2NR 是全局行号(跨文件累加),FNR 是当前文件行号(每个文件从 1 开始)。想跳过每个文件的标题行用 FNR,用 NR 会跳过 file1 后也跳过 file2 的第一行。
8.sed 的 -i 在 macOS 下必须加备份后缀
sed -i 's/foo/bar/g' file.txtsed -i '' 's/foo/bar/g' file.txt # macOS 或 sed -i.bak '...' file.txtBSD sed(macOS 默认)的 -i 要求紧跟一个备份后缀参数,空字符串 '' 表示不备份。Linux 的 GNU sed 允许 -i 后直接跟命令。跨平台脚本务必注意此差异。
9.find 的 -exec 中 {} 位置不当导致命令失败
find . -name '*.jpg' -exec cp {} /backup/find . -name '*.jpg' -exec cp {} /backup/ \;漏掉结尾的 \; 或 + 会让 find 把后续内容当作 -exec 的参数。正确写法必须用 \; 或 + 明确结束。若用 + 则 {} 必须出现在命令末尾。
10.grep 匹配二进制文件输出乱码
grep 'pattern' binary_filegrep -a 'pattern' binary_file # 或 grep -I 'pattern' binary_file默认 grep 对二进制文件只输出 'Binary file matches' 不显示内容。加 -a 强制按文本处理,加 -I 忽略二进制文件(适合在大量文件中搜索时避免乱码)。
grep -E 'pattern' file | awk '{print $N}' | sed 's/old/new/g'
pattern正则表达式,匹配目标文本file输入文件或标准输入Nawk 字段编号,从 1 开始oldsed 替换前的字符串newsed 替换后的字符串日志文件 access.log 中提取状态码 500 的请求 IP:grep ' 500 ' access.log | awk '{print $1}' | sed 's/\[.*\]//g'。假设日志行 '192.168.1.1 - - [10/Oct/2023:13:55:36] "GET /api" 500 123',grep 匹配含 ' 500 ' 的行,awk 取第 1 字段得 '192.168.1.1',sed 删除方括号及内容(此处无影响),最终输出 '192.168.1.1'。
纯浏览器端(FE 实现)处理文件时,整个文件会加载到内存里,如果文件超过几百 MB 或 GB 级,浏览器可能假死甚至崩溃。建议先用 `split` 或 `head -n 1000` 切出小段样本测试命令,确认正则无误后再对全量文件用。本工具适合快速验证语法,生产环境大文件还是用终端原生命令更稳。
常见原因有:1)字段分隔符(FS)不一致,比如终端默认是空格/制表符,但工具输入里混了全角空格或逗号,需要显式指定 `-F','` 或 `-F'\t'`。2)换行符问题:Windows 的 CRLF 会导致 `$NF` 多一个 `\r`,建议先用 `sed 's/\r//'` 清洗。3)编码差异:UTF-8 和 GBK 混用会让正则匹配失败。粘贴前确认文本编码一致。
本工具是前端模拟,不会直接修改原始上传的文件。所有替换结果都只显示在输出框里,需要手动复制或下载。`-i` 选项(原地替换)在浏览器沙箱里无法真正生效,所以输出框展示的是替换后的内容,原文件不受影响。如果希望覆盖,请手动把输出结果粘贴回原文件。
`-name` 区分大小写,`-iname` 忽略大小写。比如搜 `*.log` 时 `-name` 只匹配小写 .log,不匹配 .LOG 或 .Log。另外通配符 `*` 只匹配当前目录层,不会递归进子目录,需要加 `-depth` 或 `-maxdepth` 控制层次。如果文件路径包含空格或特殊字符,记得用双引号把模式包起来。
基础正则(BRE)里 `+` `?` `{` `|` `(` `)` 这些元字符需要反斜杠转义才能生效,比如匹配一个或多个字符要写 `\+`。扩展正则(ERE)则直接写 `+` 即可,不用转义。本工具默认用 ERE(-E),所以写 `grep -E 'ab+c'` 就行,加转义反而匹配字面加号。如果不加 -E,`ab+c` 只会匹配字面 `ab+c` 这个字符串。
`$0` 代表整行(包括所有字段),`$1` 是第一列,`$2` 第二列,依此类推。`NF` 是当前行的字段数,所以最后一列是 `$NF`。打印第二列和最后一列:`awk '{print $2, $NF}'`。注意字段默认按空白(空格/制表符)分割,如果分隔符是逗号,要先设 `-F','`。
删除行的语法是 `sed '3,5d'`(d 是删除命令),注意 d 前面没有冒号或空格。常见错误:写成 `sed '3,5 d'`(空格导致语法错误)或 `sed '/3,5/d'`(斜杠会被当成正则,匹配包含字符串 `3,5` 的行)。正确写法直接写在输入框里,输出就会显示删掉 3-5 行后的结果。
用 `-path` 和 `-prune` 组合。排除 `./node_modules` 目录:`find . -path './node_modules' -prune -o -name '*.js' -print`。注意顺序:先 `-path` 匹配要排除的目录,`-prune` 阻止进入该目录,`-o` 表示否则,后面接正常查找条件。`-path` 的模式要写相对路径(从查找起点开始),不是绝对路径。
隐私保证所有计算与处理均在你的浏览器本地完成,输入数据不会上传服务器,也不会保存或共享。