CLI AI

Count occurrences in a log: grep -c is not the answer

2026-08-11

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
$ 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
$ 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
$ 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
$ 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
$ 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
$ 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 -c does not count occurrences. Verified on one file: -c says 3, -o | wc -l says 4. When a line matches twice, -c can't see it. Decide which question you are asking — "how many lines" or "how many times".
  • grep -o with no matches prints nothing and exits 1. Piped into wc -l that looks like an honest zero, but the exit status is non-zero and a script running under set -e dies right there.
  • cut -c positions 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

CliAI turns "how many times" and "how many lines" into two different commands, and shows which one it picked. Install it in one line.