grep -c ERROR app.log counts lines that contain the word; grep -o ERROR app.log | wc -l counts how many times the word actually appears. On the same file those are 3 and 4, and the difference is a single line holding two errors. Which number you want depends on the question you meant to ask.
1. How many lines contain the word
$ clai how many lines in app.log contain ERROR→ grep -c ERROR app.log3
-c counts matching lines, not matches. A line where ERROR appears twice still counts as one.
2. How many times the word appears
$ clai how many times does ERROR appear in app.log→ grep -o ERROR app.log | wc -l4
3 against 4 on the very same file. -o prints every match on its own line, and only then does wc -l count occurrences. The extra one is the line with two errors on it.
3. Ignoring case
$ clai count lines with error in any case→ grep -ic error app.log4
-i picked up a fourth line — the one where the word is lowercase. In a log written by several services, casing is never guaranteed.
4. The top error messages
$ clai show which errors happen most often→ 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
This is usually the real goal. -o extracts the message, sed trims trailing spaces, uniq -c counts and the second sort -rn puts the frequent ones on top.
5. Spread over the hours
$ clai show which hours had errors→ grep ERROR app.log | cut -c12-13 | sort | uniq -c 1 09 1 10 1 11
cut -c12-13 takes the hour out of a 2026-08-10 09:12:03 timestamp. The positions depend on your log format — look at one line and count the characters before copying this.
6. Across several files at once
$ clai count errors in each log file→ grep -c ERROR *.logapp.log:3app2.log:3
With more than one file grep prints name and count separated by a colon. To rank them: grep -c ERROR *.log | sort -t: -k2 -rn.
Gotchas
grep -cdoes not count occurrences. Verified on one file:-csays 3,-o | wc -lsays 4. When a line matches twice,-ccan't see it. Decide which question you are asking — "how many lines" or "how many times".grep -owith no matches prints nothing and exits 1. Piped intowc -lthat looks like an honest zero, but the exit status is non-zero and a script running underset -edies right there.cut -cpositions are tied to your log format. The moment the logger adds milliseconds or a hostname at the start of the line, the hourly breakdown silently counts the wrong characters. For unstable formats use fields instead:awk '{print $2}'.
Related questions
How do I count whole words instead of substrings? grep -wo error — -w requires word boundaries, otherwise errors and errorlog join the count.
How do I count inside compressed logs? zgrep -c ERROR app.log.1.gz behaves like grep without unpacking the file to disk.
How do I count distinct values? Add sort -u before counting: grep -o 'ERROR [a-z ]*' app.log | sort -u | wc -l gives the number of different messages rather than their total.
See also
- Tail multiple log files at once
- journalctl: logs for one service
- Remove duplicate lines, with or without sorting
CliAI turns "how many times" and "how many lines" into two different commands, and shows which one it picked. Install it in one line.