Skip to content

How to Count Lines in a File in Linux (wc -l and More)

6 min read

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 a total line.
  • 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 file or nl -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

  1. Give wc several 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
  2. A plain * also matches folders. wc prints wc: backup: Is a directory, counts the folder as 0 and exits with code 1, which can stop a script that uses set -e. Use a pattern that matches files only, or find.
  3. 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
  4. 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
  • -v counts the lines that do not match, so '^[[:space:]]*$' with -v counts lines with real content. awk 'NF' access.log | wc -l gives the same 8.
  • -i ignores case: grep -ci 'get' access.log printed 7.
  • grep -c counts lines, not matches. A line that contains the word twice counts once. To count every occurrence, use grep -o 'word' file | wc -l, or see how to count word occurrences in a text file.
  • With several files, grep -c '' access.log error.log prints 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 -l prints 1 even when $var is empty, because echo adds a newline. printf '%s' "$var" | wc -l printed 0 for an empty variable, but counts one short for text without a final newline, so test both cases in your script.
  • A while read loop skips a last line without a newline: our loop counted 2 lines in the 3-line file. Write it as while IFS= read -r line || [ -n "$line" ]; do to 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 file numbers 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 file numbers every line.
  • grep -n 'pattern' file prints matching lines with their line numbers, for example 3: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.

Related services

KVM VPS hosting

NVMe SSD and full root access, with Free Basic Managed Support.

Unmanaged dedicated servers

full root access and free IPMI KVM, with Free Basic Managed Support.

Linux server management

hardening, patching, monitoring and automation for Ubuntu, Debian, RHEL and AlmaLinux.

Was this answer helpful?

Still stuck? Open a ticket