wc Command Explained: Counting Lines, Words, and Bytes

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.