grep -c ERROR app.log 数的是包含该词的行数;grep -o ERROR app.log | wc -l 数的才是这个词真正出现了多少次。同一个文件里它们分别是 3 和 4,差别就是某一行里有两个错误。你要哪个数字,取决于你本来想问什么。
1. 有多少行包含这个词
$ clai app.log 里有多少行包含 ERROR→ grep -c ERROR app.log3
-c 统计的是匹配行,不是匹配次数。一行里出现两次 ERROR,仍然只算一行。
2. 这个词出现了多少次
$ clai ERROR 在 app.log 里出现了多少次→ grep -o ERROR app.log | wc -l4
同一个文件,3 对 4。-o 会把每个匹配单独打印成一行,这时 wc -l 数的才是出现次数。多出来的那个,正是有两个错误的那一行。
3. 忽略大小写
$ clai 统计包含 error 的行,不区分大小写→ grep -ic error app.log4
-i 捞出了第四行——那行里这个词是小写的。多个服务往同一个日志里写时,大小写从来没有保证。
4. 最常见的错误消息
$ clai 显示哪些错误最常出现→ grep -o 'ERROR [a-z ]*' app.log | sed 's/ *$//' | sort | uniq -c | sort -rn 2 ERROR timeout connecting to db 1 ERROR retry failed 1 ERROR permission denied
这通常才是真正的目的。-o 抽出消息文本,sed 去掉行尾空格,uniq -c 计数,后面的 sort -rn 把高频的顶上来。
5. 按小时分布
$ clai 显示哪些小时出现过错误→ grep ERROR app.log | cut -c12-13 | sort | uniq -c 1 09 1 10 1 11
cut -c12-13 从 2026-08-10 09:12:03 这样的时间戳里取出小时。位置取决于你的日志格式——照抄之前先看一行、数一数字符。
6. 一次统计多个文件
$ clai 统计每个日志文件里的错误数→ grep -c ERROR *.logapp.log:3app2.log:3
文件多于一个时,grep 会用冒号分隔文件名和计数。要排序就用:grep -c ERROR *.log | sort -t: -k2 -rn。
Gotchas
grep -c数的不是出现次数。 在同一个文件上验证过:-c说 3,-o | wc -l说 4。一行里匹配两次时,-c看不见。先想清楚你问的是"多少行"还是"多少次"。grep -o没有匹配时不输出且退出码为 1。 接进wc -l看起来像个老实的 0,但退出状态非零,开了set -e的脚本会正好死在这里。cut -c的位置绑死了日志格式。 一旦日志器在行首加上毫秒或主机名,按小时的拆分就会悄悄数错字符。格式不稳定时改用字段:awk '{print $2}'。
Related questions
怎么只统计完整单词而不是子串? grep -wo error——-w 要求词边界,否则 errors 和 errorlog 也会被算进去。
压缩过的日志怎么统计? zgrep -c ERROR app.log.1.gz 用法和 grep 一样,而且不会把文件解压到磁盘上。
怎么统计有多少种不同的值? 计数前加一个 sort -u:grep -o 'ERROR [a-z ]*' app.log | sort -u | wc -l 给出的是不同消息的种类数,而不是总次数。
See also
CliAI 会把"多少次"和"多少行"变成两条不同的命令,并告诉你它选了哪一条。一行安装。