Short answer: the wc command counts lines, words and bytes. wc file prints all three plus the file name, wc -l file only the lines, -w words, -c bytes, -m characters and -L the longest line. wc -l < file prints the bare number for scripts. -l counts newline characters, so a last line without one is not counted.
wc -l /var/log/nginx/access.log # how many requests
find /var/www -type f | wc -l # how many files under a directory
grep -c ERROR app.log # how many lines match (no wc needed)
lines=$(wc -l < data.csv) # just the number, into a variableEvery example with output below was run on Debian 12 with GNU coreutils 9.1, the version Debian 12 ships, in a scratch directory against sample files; the output is copied from those runs.
wc cheat sheet
| Option | Counts | Example |
|---|---|---|
-l, --lines | Newline characters (lines) | wc -l file |
-w, --words | Words: runs of non-whitespace | wc -w file |
-c, --bytes | Bytes | wc -c file |
-m, --chars | Characters in the current locale | wc -m file |
-L, --max-line-length | Display width of the longest line | wc -L file |
--files0-from=F | Files named in F, separated by NUL bytes | find . -name '*.py' -print0 | wc -l --files0-from=- |
| (no option) | Lines, words and bytes | wc file |
Reading wc’s output
For this sample file (four lines, the third one empty, with the non-ASCII word Größe):
$ cat notes.txt
The quick brown fox
jumps over the lazy dog.
Größe: 42 MB
$ wc notes.txt
4 12 61 notes.txt
$ wc -l notes.txt
4 notes.txt
$ wc -wl notes.txt
4 12 notes.txtThe columns are lines, words, bytes, then the file name. The order is fixed whatever order you type the flags in: wc -wl and wc -lw both print lines first, then words, and with every option the order is lines, words, characters, bytes, longest line. With more than one count, GNU wc pads the numbers to a common width, which is why the lines above start with a space.
wc -l: counting lines, and the missing last line
wc -l counts newline characters, not lines in the everyday sense. A file whose last line has no newline at the end, common for files written by editors on Windows or by printf in a script, comes out one short:
$ printf 'one\ntwo\nthree' > nonl.txt
$ wc -l nonl.txt
2 nonl.txt
$ grep -c '' nonl.txt
3
$ awk 'END {print NR}' nonl.txt
3grep -c '' counts every line including an unterminated last one, and so does awk’s record counter. An empty file is 0 for all of them. For log files and anything a program wrote line by line this never matters, because every line ends with a newline; for hand-edited files and data exports, check.
wc -w: what counts as a word
GNU wc defines a word as a non-zero-length sequence of printable characters delimited by white space. Punctuation is part of the word, and hyphens do not split anything:
$ echo 'e-mail foo--bar dog. ,' | wc -w
4e-mail, foo--bar, dog. and the lone comma are four words. That is good enough for a rough length of a text, but not a linguistic word count.
-c vs -m: bytes and characters
$ wc -c notes.txt
61 notes.txt
$ wc -m notes.txt
59 notes.txt
$ LC_ALL=C wc -m notes.txt
61 notes.txtö and ß take two bytes each in UTF-8, so the file has 61 bytes but 59 characters. -m depends on the locale: in the plain C locale every byte counts as a character and -m equals -c. For the size of a file in bytes, wc -c < file is a portable answer; for checking whether a string fits a character limit, use -m in a UTF-8 locale.
-L: the longest line
$ wc -L notes.txt
24 notes.txt
$ printf 'a\tb\n' | wc -L
9-L reports display width, not bytes: a tab advances to the next multiple of 8, so a, a tab and b are 9 columns wide. Useful for finding the line that breaks a fixed-width layout or a config file’s line limit.
Several files and the total line
$ wc -l access.log error.log
3 access.log
2 error.log
5 total
$ wc -l *.log | tail -n 1
5 total
$ cat *.log | wc -l
5With more than one file, wc adds a total line. tail -n 1 keeps only that, and cat *.log | wc -l prints the plain sum. Newer coreutils (9.2 and later, for example on Ubuntu 24.04 and Debian 13) add --total=auto|always|only|never for the same job; Debian 12’s 9.1 does not have it. wc --help | grep -- --total prints the option where it exists and nothing where it does not (a plain grep total also matches the help text about the total line).
A missing file does not stop the others from being counted, but it changes the exit status, which matters in scripts:
$ wc -l access.log nosuch.log
3 access.log
wc: nosuch.log: No such file or directory
3 total
$ echo $?
1A directory gives wc: DIR: Is a directory and a count of 0. To count lines across many files whose names may contain spaces, let find hand them over separated by NUL bytes:
$ find src -name '*.py' -print0 | wc -l --files0-from=-
2 src/main.py
1 src/lib/my util.py
3 totalJust the number: wc -l < file
$ wc -l < app.log
5
$ n=$(wc -l < app.log)
$ [ "$n" -gt 3 ] && echo "more than 3 lines"
more than 3 linesReading from standard input instead of a file name means wc has no name to print, so the output is the bare number, ready for a variable or a comparison. That is cleaner than wc -l file | cut -d' ' -f1, which breaks as soon as the output is padded. One portability note: the BSD wc on macOS pads even a single number with leading spaces. $(( $(wc -l < file) )) turns it into a clean integer in any POSIX shell.
Counting files: find, not ls
ls | wc -l is the answer you see everywhere, and it is wrong more often than it looks. In a test directory with three images, a hidden file, a subdirectory and a fourth image whose name contains a newline:
$ ls | wc -l # 6: counts the directory, skips the hidden file, splits the odd name
6
$ ls -l | wc -l # 7: adds the "total" header line
7
$ find . -maxdepth 1 -type f | wc -l # 6: correct files, but the newline name counts twice
6
$ find . -maxdepth 1 -type f -printf . | wc -c # 5: one dot per file, always right
5ls leaves out hidden files, counts directories as entries and writes a name with a newline in it as two lines when piped. find -type f fixes the first two; printing one character per file with -printf . and counting bytes with wc -c fixes the last one too. Drop -maxdepth 1 to include subdirectories, and add a test to count only some files:
find /var/www/uploads -type f -name '*.jpg' -printf . | wc -c # JPEGs, recursively
find /var/www -mindepth 1 -type d | wc -l # subdirectories
find /var/log -type f -mtime -1 | wc -l # files changed in the last 24 hoursThe find command guide covers the tests (-name, -size, -mtime, -prune) that narrow what gets counted.
Counting matches: grep -c vs grep | wc -l
$ grep -c ERROR app.log # lines containing ERROR
2
$ grep ERROR app.log | wc -l # the same, with an extra process
2
$ grep -o ERROR app.log | wc -l # occurrences: one line has ERROR twice
3
$ grep -c ERROR app.log app2.log # per file
app.log:2
app2.log:1
$ cat app.log app2.log | grep -c ERROR
3grep -c counts matching lines, so a line with two hits counts once; grep -o prints each hit on its own line, and wc -l then counts occurrences. With several files grep -c reports per file, so pipe the files through cat (or use grep -h ... | wc -l) for one total. The grep command guide has the flags that shape what matches, such as -i, -w and -v.
Counting things on a server
wc -l at the end of a pipeline turns any list into a number. The ones that come up most on a VPS:
ps -e --no-headers | wc -l # processes
ss -Htan state established | wc -l # open TCP connections
getent passwd | wc -l # user accounts
cut -d' ' -f1 /var/log/nginx/access.log | sort -u | wc -l # unique client IPs
zcat /var/log/nginx/access.log.2.gz | wc -l # requests in a rotated, compressed logRun wc on the uncompressed stream, never on the .gz file itself: wc -l access.log.2.gz counts newline bytes that happen to occur in the compressed data, a meaningless number. For a growing log, wc -l is a snapshot; to watch new lines arrive, use tail -f. The Linux commands cheat sheet lists where wc sits among the other text tools.
The Windows equivalent
Windows has no wc. In PowerShell, Measure-Object does the same job, per Microsoft’s documentation; in Command Prompt, find can count lines:
Get-Content .\app.log | Measure-Object -Line -Word -Character # PowerShell
(Get-Content .\app.log | Measure-Object -Line).Lines # just the number
find /c /v "" app.log # cmd: count all lines- Need the lines themselves, not the count?
head,tailandgrepprint them;wconly ever prints numbers. - Counting lines of a huge file is cheap.
wc -lonly scans for newline bytes and reads the file in blocks, so even a multi-gigabyte log never has to fit into memory.
Frequently asked
How do I count the lines in a file on Linux?
wc -l file prints the line count followed by the file name, and wc -l < file prints only the number. wc -l counts newline characters, so a last line without a trailing newline is not counted; grep -c '' file counts it.
How do I count the files in a directory?
find DIR -type f | wc -l counts regular files recursively, and adding -maxdepth 1 limits it to the top level. If file names might contain newlines, find DIR -type f -printf . | wc -c is exact. ls | wc -l also counts directories, skips hidden files and miscounts unusual names.
What is the difference between wc -c and wc -m?
wc -c counts bytes and wc -m counts characters in the current locale. In UTF-8, letters such as ö or ß take two bytes, so -c is larger than -m for such text. In the C locale both give the same number.
Is grep -c the same as grep | wc -l?
For one file, yes: both count matching lines, not matches. grep -c saves a process and prints one count per file when given several files. To count every occurrence instead of every matching line, use grep -o PATTERN file | wc -l.