查看日志时,经常需要找某个关键字,或者从一大段输出里筛选出几行。这个时候用 grep 就很方便。它按模式匹配文本,默认输出匹配到的整行,不修改文件。
基本形式:
grep '匹配模式' 文件名也可以放在管道后面,处理前一个命令的输出。下面用几个例子看看常用写法。
准备示例文本
把下面内容保存为 app.log,后面的命令都使用这个文件:
INFO service started
INFO request id=100 path=/api/users
WARN request id=101 path=/api/orders timeout
ERROR request id=102 connection refused
INFO retry id=102
ERROR request id=103 timeout timeout
DEBUG errorCount=2
INFO request id=104 path=/api/users查找包含 ERROR 的行:
grep 'ERROR' app.logERROR request id=102 connection refused
ERROR request id=103 timeout timeout即使匹配的只是 ERROR,输出也包含这一整行。grep 默认区分大小写,所以第 7 行的 errorCount 没有被选中。
忽略大小写,显示行号
加上 -i 忽略大小写,再加 -n 显示行号:
grep -in 'error' app.log4:ERROR request id=102 connection refused
6:ERROR request id=103 timeout timeout
7:DEBUG errorCount=2这时 errorCount 也匹配到了,因为 grep 默认不要求匹配整个单词。
如果只想找独立的 error,可以使用 -w:
grep -inw 'error' app.log4:ERROR request id=102 connection refused
6:ERROR request id=103 timeout timeout-w 判断的是单词边界,字母、数字和下划线都算单词字符。比如 error_count 中的 error 也不会单独匹配。
排除某些行
-v 反向选择,输出不匹配的行。去掉以 INFO 开头的记录:
grep -v '^INFO ' app.logWARN request id=101 path=/api/orders timeout
ERROR request id=102 connection refused
ERROR request id=103 timeout timeout
DEBUG errorCount=2^ 表示行首,$ 表示行尾。比如 ^INFO service started$ 就要求这一整行完全匹配。
也可以通过管道组合条件,找出同时包含 ERROR 和 timeout 的行:
grep 'ERROR' app.log | grep 'timeout'ERROR request id=103 timeout timeout匹配多个模式
想同时查看 WARN 和 ERROR,可以用多个 -e。任意一个模式匹配,这一行就会输出:
grep -e '^WARN ' -e '^ERROR ' app.logWARN request id=101 path=/api/orders timeout
ERROR request id=102 connection refused
ERROR request id=103 timeout timeout使用 -E 开启扩展正则表达式,也可以写成:
grep -E '^(WARN|ERROR) ' app.log结果相同。这里括号把两个分支放在一起,| 表示“或者”,行首限制对两个分支都生效。
按普通字符串查找
grep 默认把模式当成正则表达式。查找带有点号、方括号等字符的内容时,如果只想按原样匹配,可以加 -F:
printf 'server=api.example.com\nserver=apiXexampleYcom\n' | grep -F 'api.example.com'server=api.example.com如果去掉 -F,正则里的点号可以匹配任意单个字符,第二行也会被选中。
看一下前后文
只有报错那一行,有时还看不出发生了什么。-B 显示匹配行之前的几行,-A 显示之后的几行,-C 则同时显示前后文。
查找连接失败的记录,连同前后各一行一起输出:
grep -n -C 1 'connection refused' app.log3-WARN request id=101 path=/api/orders timeout
4:ERROR request id=102 connection refused
5-INFO retry id=102这里匹配行的行号后是冒号,上下文行的行号后是连字符。
统计行数和匹配次数
-c 统计的是匹配的行数:
grep -c 'timeout' app.log2第 6 行虽然出现了两次 timeout,仍然只算一行。
如果要统计这个字符串出现了几次,可以用 -o 只输出匹配到的部分,每次非空匹配单独占一行:
grep -o 'timeout' app.logtimeout
timeout
timeout再接上 wc -l:
grep -o 'timeout' app.log | wc -l结果是 3。这里统计的是不重叠的匹配次数,不能把 -c 直接当成出现次数。
在目录中查找
日志分散在多个文件里时,可以递归查找。下面只检查 logs 目录下文件名以 .log 结尾的文件:
grep -rn --include='*.log' 'ERROR' logs/输出会带上文件名和行号。如果只想知道哪些文件包含 ERROR,不需要匹配内容,把 -n 换成 -l:
grep -rl --include='*.log' 'ERROR' logs/-l 输出的是文件名,每个匹配文件只列一次;--include 后面的通配符加上引号,交给 grep 处理。
在脚本中判断
只关心有没有匹配时,用 -q,不输出内容:
if grep -q 'ERROR' app.log; then
echo '发现 ERROR 日志'
fi输出:
发现 ERROR 日志grep 通常以状态码 0 表示匹配到,1 表示没有匹配,2 表示执行出错。脚本里的 if 判断的是状态码,不是屏幕上有没有输出。