Guides

wc command: count lines, words, bytes and files on Linux

The wc command counts lines, words and bytes: wc -l, -w, -c, -m, -L, totals, wc -l < file, counting files with find, and grep -c vs grep | wc -l.

Published ~7 min read
On this page

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.

any Linux
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 variable

Every 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

OptionCountsExample
-l, --linesNewline characters (lines)wc -l file
-w, --wordsWords: runs of non-whitespacewc -w file
-c, --bytesByteswc -c file
-m, --charsCharacters in the current localewc -m file
-L, --max-line-lengthDisplay width of the longest linewc -L file
--files0-from=FFiles named in F, separated by NUL bytesfind . -name '*.py' -print0 | wc -l --files0-from=-
(no option)Lines, words and byteswc file

Reading wc’s output

For this sample file (four lines, the third one empty, with the non-ASCII word Größe):

any Linux
$ 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.txt

The 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:

any Linux
$ printf 'one\ntwo\nthree' > nonl.txt
$ wc -l nonl.txt
2 nonl.txt

$ grep -c '' nonl.txt
3

$ awk 'END {print NR}' nonl.txt
3

grep -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:

any Linux
$ echo 'e-mail foo--bar  dog. ,' | wc -w
4

e-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

any Linux
$ 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

any Linux
$ 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

any Linux
$ 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
5

With 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:

any Linux
$ wc -l access.log nosuch.log
3 access.log
wc: nosuch.log: No such file or directory
3 total
$ echo $?
1

A 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:

any Linux
$ find src -name '*.py' -print0 | wc -l --files0-from=-
2 src/main.py
1 src/lib/my util.py
3 total

Just the number: wc -l < file

any Linux
$ wc -l < app.log
5

$ n=$(wc -l < app.log)
$ [ "$n" -gt 3 ] && echo "more than 3 lines"
more than 3 lines

Reading 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:

any Linux
$ 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
5

ls 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:

any Linux
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 hours

The find command guide covers the tests (-name, -size, -mtime, -prune) that narrow what gets counted.

Counting matches: grep -c vs grep | wc -l

any Linux
$ 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
3

grep -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:

any Linux
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 log

Run 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:

PowerShell · cmd
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, tail and grep print them; wc only ever prints numbers.
  • Counting lines of a huge file is cheap. wc -l only 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.

Related articles

All guides