wc (word count) counts lines, words and bytes. On servers it’s mostly used at the end of a pipeline to answer “how many?”: how many requests, errors, files, users or connections.
Basics
wc file.txt
# 120 843 5231 file.txt
# lines words bytes
Pick what you need:
wc -l file # lines
wc -w file # words
wc -c file # bytes
wc -m file # characters (differs from bytes for UTF-8 text)
wc -L file # length of the longest line
Several files give one line each, plus a total:
wc -l /var/log/nginx/*.log
Print only the number
With a filename, wc prints the name too. Read from stdin to get just the number, which is handy in scripts:
wc -l < access.log # 48211
lines=$(wc -l < access.log)
Counting in pipelines
grep -c ' 500 ' access.log # error count (grep can count itself)
grep ' 404 ' access.log | wc -l # same idea with wc
awk '{print $1}' access.log | sort -u | wc -l # unique visitor IPs
ss -tn state established | tail -n +2 | wc -l # open TCP connections
ps aux | grep -c '[p]hp-fpm' # PHP-FPM processes
getent passwd | wc -l # user accounts
The [p]hp-fpm trick stops grep from counting its own process.
Counting files:
find /var/www/uploads -type f | wc -l # files, recursively
find . -maxdepth 1 -type f | wc -l # files in this directory only
Avoid ls | wc -l for this. It counts names, and filenames containing newlines throw it off. See ls and find.
Lines of code in a project:
find src -name '*.py' -print0 | xargs -0 cat | wc -l
For real code statistics, cloc or tokei separate code, comments and blank lines.
Gotchas
The last line has no newline. wc -l counts newline characters. A file whose last line doesn’t end in a newline shows one line fewer than you’d expect.
Bytes vs characters. wc -c counts bytes. For UTF-8 text, accented letters and CJK characters take 2–4 bytes, so use wc -m to count characters. The result depends on your locale (LANG=C.UTF-8).
Words are whitespace-separated runs. wc -w counts foo-bar as one word and foo bar as two. It’s fine for rough counts, not for precise text analysis.
Header lines. Commands like ps, ss and df print a header. Strip it with tail -n +2 before counting, or the count is one too high.
Live log files keep growing, so the count is a snapshot.
Watch a count change
Combine with watch to monitor something:
watch -n 2 'ss -tn state established | tail -n +2 | wc -l'
See watch.
Compressed logs
zcat access.log.2.gz | wc -l
zgrep -c ' 500 ' access.log.*.gz
Byte size of a file
wc -c reads the whole file. For large files, stat is instant:
stat -c %s bigfile.iso
du -h bigfile.iso
Quick reference
| Task | Command |
|---|---|
| Lines | wc -l file |
| Number only | wc -l < file |
| Words | wc -w file |
| Bytes / characters | wc -c / wc -m |
| Longest line | wc -L file |
| Count matches | grep -c pattern file |
| Count unique values | … | sort -u | wc -l |
| Count files | find dir -type f | wc -l |
Related: sort and uniq, grep and cut.