Table of Contents
Back to Blog

AWK Guide

How to Count Lines in AWK (NR, FNR, and Why They're Different With Multiple Files)

Count lines in AWK with END{print NR}, per-file FNR patterns, RS record separator changes, and match counting. Covers NR vs FNR, GNU awk ENDFILE, nonblank-line counting, and when wc -l is the better tool.

POSIX awkGNU awkUnix Text Tools
Published: May 14, 2026Updated: May 14, 202610 min readAuthor: Line Counter Editorial Team
AWKUnixLinuxText ProcessingTutorial

The most famous awk count lines one-liner is still the right place to start:

awk 'END{print NR}' file.txt

For a single file, that is clean, portable, and correct. NR is the number of records AWK has read, and with the default record separator, records are lines.

The trouble starts when people assume that the same idea scales unchanged to multiple files.

If you run:

awk 'END{print NR}' file1.txt file2.txt

you do not get one count per file. You get the total across both files. That is the heart of the awk NR count lines trap.

There is a second trap right behind it: once you change awk RS record separator, you may not be counting lines anymore at all. You may be counting paragraphs, CSV-style records, or custom chunks.

This guide keeps those jobs separate:

  • awk count lines in file with NR
  • awk FNR multiple files patterns for per-file output
  • awk count lines matching pattern
  • awk RS record separator behavior when "line" stops meaning newline
  • awk vs wc -l when performance matters more than flexibility

30-Second Cheat Sheet

  • Count total lines in one file: awk 'END{print NR}' file.txt
  • Count total lines from stdin: some_command | awk 'END{print NR}'
  • Count lines per file in input order: awk 'FNR==1{if(NR>1) print prev, n; prev=FILENAME} {n=FNR} END{if(NR) print prev, n}' files...
  • Count matching lines: awk '/pattern/{c++} END{print c+0}' file.txt
  • Count nonblank lines: awk 'NF{c++} END{print c+0}' file.txt
  • Fastest raw count: wc -l < file.txt

If all you need is awk count lines in file, stop at END{print NR}. The rest of this article is about the cases where that simple answer stops being enough.

Method 1: END{print NR} for a Single File

This is the core awk count lines pattern:

awk 'END{print NR}' file.txt

Why it works:

  • NR is the running number of input records
  • by default, RS is a newline
  • therefore, each record is one physical line

You can use the same logic on standard input:

cat file.txt | awk 'END{print NR}'
printf 'a\nb\nc\n' | awk 'END{print NR}'

That makes awk count lines in file work equally well for files, pipes, and generated text streams.

There is also a practical edge over wc -l: AWK counts the last unterminated line as a record. So for a file that does not end with a trailing newline, awk 'END{print NR}' still returns the logical line count, while wc -l only counts newline characters.

If your job is only awk count lines in file, this section is enough. If you pass multiple files, keep reading.

Method 2: NR vs FNR With Multiple Files

This is the awk FNR multiple files rule that matters:

  • NR keeps increasing across every input file
  • FNR resets to 1 when AWK starts a new file

Example:

awk '{print FILENAME, "NR=" NR, "FNR=" FNR}' file1.txt file2.txt

You will see NR continue upward while FNR restarts at each new file.

That means this:

awk 'END{print NR}' file1.txt file2.txt

prints the combined total, not per-file counts.

The common wrong shortcut

People often try this:

awk 'END{print FILENAME, FNR}' file1.txt file2.txt

That only prints the last file and its final FNR. It does not give you one line per input file.

The portable per-file pattern

For ordered per-file counts, use FNR explicitly:

awk '
  FNR == 1 {
    if (NR > 1)
      print prev_file, prev_count
    prev_file = FILENAME
  }
  { prev_count = FNR }
  END {
    if (NR > 0)
      print prev_file, prev_count
  }
' file1.txt file2.txt

That is the safest awk FNR multiple files pattern when you want output in the same order as the file arguments.

The GNU awk shortcut

If GNU awk is available, ENDFILE is cleaner:

gawk 'ENDFILE { print FILENAME, FNR }' file1.txt file2.txt

That is the neatest gawk ENDFILE count lines form, but it is GNU awk only. On this machine, for example, awk is mawk, and gawk is not installed.

The important compatibility point is this:

  • FNR is standard in modern POSIX awk
  • ENDFILE is not

So if your script must run on plain awk, write the portable version first.

Method 3: RS Changes What a "Line" Means

AWK does not really count "lines." It counts records.

That distinction matters because awk RS record separator defines what a record is.

With the default separator, records are lines:

awk 'END{print NR}' file.txt

But if you change RS, NR changes meaning with it.

Paragraph mode

This is the classic trap:

awk 'BEGIN { RS = "" } END { print NR }' paragraphs.txt

With RS="", AWK treats one or more blank lines as the separator between records. So NR becomes a paragraph counter, not a line counter.

In local testing, a file with seven physical lines grouped into three paragraphs returned 3 with RS="" and 7 with the default RS.

The practical rule

If you want awk count lines, do not touch RS.

If you touch RS, you are deliberately counting records of some other shape.

GNU awk also supports richer RS behavior than portable POSIX awk. That can be useful, but it moves you farther away from the simple meaning of awk count lines in file.

Method 4: Count Matching and Nonblank Lines

This is where AWK becomes more useful than wc -l.

Count matching lines

The standard awk count lines matching pattern pattern is:

awk '/ERROR/{count++} END{print count+0}' app.log

The +0 matters because AWK otherwise prints an empty value when nothing matched.

You can invert the pattern too:

awk '!/ERROR/{count++} END{print count+0}' app.log

That is the simplest awk count lines matching pattern building block for logs, config files, and CSV prechecks.

Count nonblank lines

awk 'NF{count++} END{print count+0}' file.txt

This counts lines that contain at least one field. With the default field separator, that means it skips:

  • empty lines
  • lines made only of spaces or tabs

That is usually what people actually want when they say "non-empty lines."

If you want to count lines that contain spaces as non-empty, use the full line instead:

awk 'length($0) > 0 {count++} END{print count+0}' file.txt

Count a line range

awk 'NR >= 10 && NR <= 50 {count++} END{print count+0}' file.txt

That makes AWK useful when line counting is part of a larger filtering pass.

Method 5: awk vs wc -l

For raw totals, wc -l is usually the better tool.

That is the practical awk vs wc -l answer:

  • wc -l is dedicated to counting line breaks
  • AWK has to parse records and run the interpreter loop

On this Linux machine, using awk as mawk 1.3.4 and a synthetic 110 MB file with 10,000,000 lines, the results were:

CommandTimeNotes
wc -l < big.txt0.13sfastest raw line count
awk 'END{print NR}' big.txt0.33sabout 2.5x slower here
sed -n '$=' big.txt0.53sworkable, but slower again

These are local numbers, not universal ones. The exact gap depends on the AWK implementation, the filesystem cache state, and the storage stack.

So the awk vs wc -l choice is simple:

  • use wc -l for the fastest plain count
  • use AWK when the count is only one part of the job

If you need awk count lines matching pattern, per-file counts, or custom record logic, AWK earns its overhead. If you only need the number, wc -l is still the Unix default for a reason.

Method 6: Practical AWK Line Count Snippets

Here are the patterns worth keeping.

Count lines per file in input order

awk '
  FNR == 1 {
    if (NR > 1)
      print prev_file, prev_count
    prev_file = FILENAME
  }
  { prev_count = FNR }
  END {
    if (NR > 0)
      print prev_file, prev_count
  }
' *.log

Count matching lines per file

awk '
  /ERROR/ { count[FILENAME]++ }
  END {
    for (file in count)
      print file, count[file] + 0
  }
' *.log
awk 'NR % 1000000 == 0 { print "Processed", NR, "lines" } END { print "Total", NR }' huge.log

GNU awk one-liner for per-file totals

gawk 'ENDFILE { print FILENAME, FNR }' *.txt

That gawk ENDFILE count lines shortcut is great when GNU awk is guaranteed, but do not drop it into generic /usr/bin/awk scripts blindly.

Compatibility Notes

There is some old Unix folklore around FNR that is worth cleaning up.

Historically, very old awk versions predate features that later became normal. But in modern terms:

  • POSIX awk defines NR
  • POSIX awk also defines FNR
  • GNU awk adds hooks such as BEGINFILE and ENDFILE

So the modern portability boundary is not really FNR. It is the GNU-specific extras around it.

If you are shipping scripts to unknown systems, this is the right mental model:

  • awk 'END{print NR}' is universally safe
  • FNR is safe on modern POSIX-style awk implementations
  • gawk ENDFILE count lines is a GNU awk feature and should be treated that way

Sources Checked

I also verified the multi-file NR/FNR behavior, the RS="" paragraph behavior, and the END{print FILENAME, FNR} last-file trap locally with mawk 1.3.4 on Linux.

Need to count lines without writing AWK patterns?

Paste the file into the Line Counter. No NR versus FNR confusion. No RS traps. Just the number.

Frequently Asked Questions

How do I count lines in AWK?

For one file or one input stream, use awk 'END{print NR}' file.txt. That is the classic awk count lines pattern as long as RS is still the default newline.

What is the difference between NR and FNR in AWK?

NR is the total number of records seen across all input files. FNR is the record number inside the current file, so it resets when AWK starts the next file.

How do I count lines per file in AWK?

Use FNR-aware logic or, if GNU awk is available, ENDFILE. Do not use END{print FILENAME, FNR} when you pass multiple files, because that only reports the last file.

Does changing RS change line counting in AWK?

Yes. AWK counts records, and RS defines what a record is. With RS="", AWK counts paragraphs separated by blank lines instead of physical lines.

Is FNR a GNU awk-only feature?

No. Modern POSIX awk defines FNR. The GNU awk-specific part here is ENDFILE, not FNR itself. If you target very old pre-POSIX awk, test before relying on later features.

When should I use awk vs wc -l?

Use wc -l when you only need the total line count. Use awk when you need to count and filter in the same pass, such as matching lines, per-file counts, or range-limited counts.

Related Guides