wc Command Explained: Counting Lines, Words, and Bytes
wc counts things. In practice it counts lines, at the end of a pipeline, and the rest of its features exist mostly for completeness.
The basics
wc file.txt
# 120 845 5231 file.txt
# lines words bytes
Individually:
wc -l file.txt # lines
wc -w file.txt # words
wc -c file.txt # bytes
wc -m file.txt # characters
wc -L file.txt # length of the longest line
Where it is actually used
At the end of a pipeline, answering “how many”:
# How many processes is nginx running
ps aux | grep -c nginx
# How many files match
find /var/log -name '*.gz' | wc -l
# How many unique IPs hit the server
awk '{print $1}' access.log | sort -u | wc -l
# How many lines of code
find . -name '*.py' -exec cat {} + | wc -l
Note the first example uses grep -c rather than grep | wc -l. When you only want the count, grep -c is faster because it never produces the matching lines.
The trailing newline trap
wc -l counts newline characters, not lines.
printf 'one\ntwo\nthree' | wc -l
# 2
printf 'one\ntwo\nthree\n' | wc -l
# 3
Both contain three lines by any reasonable reading. The first has two newlines because the last line is unterminated.
This matters when counting output from a program that does not terminate its final line, and it is why generated files sometimes come up one short. If it matters:
grep -c '' file.txt # counts lines, including an unterminated last one
Bytes versus characters
printf 'café\n' | wc -c # 6, because é is two bytes in UTF-8
printf 'café\n' | wc -m # 5
For English text they agree. For anything else they do not, and picking the wrong one produces subtly wrong output in scripts that align columns or check limits.
Counting files, carefully
ls | wc -l # breaks on filenames with newlines
find . -maxdepth 1 -type f | wc -l # same problem
find . -maxdepth 1 -type f -printf '.' | wc -c # reliable
Filenames can contain newlines. It is rare and it is the sort of thing that breaks a script once, at the worst possible moment. Our find builder generates the safer forms.
Multiple files
wc -l *.log
# 120 app.log
# 340 error.log
# 460 total
The total line is convenient interactively and an annoyance in scripts, where you usually want just the number:
wc -l < file.txt # no filename in the output, just the count
Redirecting rather than passing the filename is the trick worth remembering, because wc -l file.txt prints the name alongside the number and breaks naive parsing.
Frequently Asked Questions
What does wc stand for?
Word count, which undersells it. It counts lines, words, characters, and bytes, and the line count is what people use it for the overwhelming majority of the time.
Why does wc -l report one fewer line than I expect?
Because it counts newline characters, not lines. A file whose last line has no trailing newline is counted one short. Text editors usually add the final newline, which is why the problem mostly appears with generated files.
What is the difference between -c and -m?
The -c flag counts bytes and -m counts characters. They are identical for ASCII and differ for anything multibyte, where a single UTF-8 character can occupy several bytes.
How do I count files in a directory?
Pipe ls or find into wc -l, though find is safer because filenames containing newlines break the count. Using find with -printf and counting is more reliable for scripting.
Why is wc slow on very large files?
Counting words or characters requires examining every byte. Counting lines alone is faster because it only looks for newlines. If you only need the line count, pass -l rather than letting it compute everything.
Can wc count matches from grep?
You can pipe grep into wc -l, and grep -c does it directly and faster since it counts internally without producing output. Use grep -c unless you need the matched lines for something else too.