Applies to: GNU coreutils 9.7 (Debian 13), GNU coreutils 9.5 (AlmaLinux 10.2) and uutils coreutils 0.10.0 (Ubuntu 26.04), with GNU grep, awk and sed
To count the lines in a file on Linux, run wc -l with the file name:
$ wc -l access.log 10 access.log
To get only the number (for a script or a variable), let wc read the file from standard input:
$ wc -l < access.log 10
wc -l counts newline characters. That is the right answer for almost every file, but a file whose last line has no newline at the end is counted one line short. The sections below cover that case, several files at once, counting only some lines and doing it in Bash. We ran the examples on Debian 13, Ubuntu 26.04 and AlmaLinux 10.2, and the output was the same on all three, including the Rust-based wc (uutils) that Ubuntu 26.04 uses.
Quick reference
wc -l file: lines and the file name.wc -l < file: the number only.wc -l *.log: each file plus atotalline.grep -c '' file: lines, including a last line without a newline.grep -c 'text' file: lines that contain text.grep -cv '^[[:space:]]*$' file: lines that are not empty.zcat file.gz | wc -l: lines in a compressed log.cat -n fileornl -ba file: show every line with its number.
Count lines in one file
Plain wc prints three numbers: lines, words and bytes. -l keeps only the line count.
$ wc access.log 10 56 348 access.log $ wc -l access.log 10 access.log
cat access.log | wc -l also prints 10, but wc -l < access.log does the same without starting an extra process. For file names with spaces, use quotes: wc -l "my notes.txt".
Other wc options you may need: -w counts words, -c bytes, -m characters (different from bytes in UTF-8 text with accents or symbols) and -L prints the length of the longest line.
Count lines in several files or a whole folder
- Give
wcseveral files or a wildcard. It prints a line per file and a total:$ wc -l *.log 10 access.log 10 error.log 3 short.log 23 total
- A plain
*also matches folders.wcprintswc: backup: Is a directory, counts the folder as 0 and exits with code 1, which can stop a script that usesset -e. Use a pattern that matches files only, orfind. - To include subfolders, use
find:$ find . -type f -name '*.log' -exec wc -l {} + 1 ./backup/old.log 3 ./short.log 10 ./error.log 10 ./access.log 24 total - For a single grand total over many files, join them first:
$ find . -type f -name '*.log' -exec cat {} + | wc -l 24
Watch for several "total" lines. With a long list of files, find -exec ... + and xargs start wc more than once, and each run prints its own total. In our lab, 20,000 files gave eight total lines, none of them the real sum. Either use the cat version above, or let GNU wc read the file list itself (this also worked with the uutils wc on Ubuntu 26.04):
$ find many -type f -print0 | wc -l --files0-from=- | tail -1 20000 total
To count lines of source code by language, with comments and blank lines separated, see cloc: count lines of code.
Why wc -l can be one line short
A text file normally ends with a newline. If the last line has none, wc -l does not count it. Lab file with three lines and no final newline:
$ od -c nonl.txt
0000000 o n e \n t w o \n t h r e e
0000015
$ wc -l nonl.txt
2 nonl.txt
$ grep -c '' nonl.txt
3
$ awk 'END{print NR}' nonl.txt
3
$ sed -n '$=' nonl.txt
3
If you need the number of lines as a person sees them, use grep -c '' or awk 'END{print NR}'. This happens with files saved by some editors, or written by a script that does not add a final newline. Windows line endings (\r\n) do not change the count: each line still ends in \n.
Empty files behave differently too. wc -l, grep -c '' and awk all print 0, but sed -n '$=' prints nothing, and grep -c exits with code 1 whenever it finds no line (even though it prints 0).
Count only some lines
grep -c counts the lines that match a pattern. In the lab log, one line is empty and one contains only spaces:
$ grep -c '404' access.log 2 $ grep -c '^$' access.log 1 $ grep -cv '^$' access.log 9 $ grep -cv '^[[:space:]]*$' access.log 8
-vcounts the lines that do not match, so'^[[:space:]]*$'with-vcounts lines with real content.awk 'NF' access.log | wc -lgives the same 8.-iignores case:grep -ci 'get' access.logprinted 7.grep -ccounts lines, not matches. A line that contains the word twice counts once. To count every occurrence, usegrep -o 'word' file | wc -l, or see how to count word occurrences in a text file.- With several files,
grep -c '' access.log error.logprints one count per file (access.log:10), without a total.
Count lines in compressed logs
Rotated logs are often gzipped. wc -l on the .gz file counts newline bytes in compressed data, which is meaningless (our 10-line log gave 1). Decompress on the fly instead:
$ zcat access.log.gz | wc -l 10 $ zgrep -c '' access.log.gz 10
Common log file locations lists where Linux and cPanel servers keep their logs.
Count lines in a Bash script
- Store the number in a variable:
lines=$(wc -l < access.log). With<there is no file name to strip off. - Counting lines in a variable needs care.
echo "$var" | wc -lprints1even when$varis empty, becauseechoadds a newline.printf '%s' "$var" | wc -lprinted0for an empty variable, but counts one short for text without a final newline, so test both cases in your script. - A
while readloop skips a last line without a newline: our loop counted 2 lines in the 3-line file. Write it aswhile IFS= read -r line || [ -n "$line" ]; doto count 3. mapfile -t arr < file; echo ${#arr[@]}reads the file into an array and also printed 3.
Show line numbers instead of a count
$ cat -n short.log
1 203.0.113.10 - - "GET / HTTP/1.1" 200
2 203.0.113.11 - - "GET /about HTTP/1.1" 200
3 198.51.100.7 - - "GET /wp-login.php HTTP/1.1" 404
nl filenumbers lines too, but by default it skips empty lines (blank lines get no number), so its last number can be lower than the line count.nl -ba filenumbers every line.grep -n 'pattern' fileprints matching lines with their line numbers, for example3:198.51.100.7 - - "GET /wp-login.php HTTP/1.1" 404.
Which command is fastest on big files?
wc -l. Lab example on our shared test VPS (Debian 13), a 5,000,000-line file, three runs each: wc -l used under 0.01 seconds of CPU time, grep -c '' 0.13 to 0.19 s, awk 'END{print NR}' (mawk) about 0.15 s and sed -n '$=' about 0.35 s. On a normal log file all of them finish instantly, so pick the one whose behaviour you need.
Related articles: Linux text processing commands: grep, sort, uniq, wc, cut and more, display file contents with cat, less, head and tail, 15 practical find command examples.