Table of Contents
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.
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 filewithNRawk FNR multiple filespatterns for per-file outputawk count lines matching patternawk RS record separatorbehavior when "line" stops meaning newlineawk vs wc -lwhen 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:
NRis the running number of input records- by default,
RSis 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:
NRkeeps increasing across every input fileFNRresets to1when 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:
FNRis standard in modern POSIX awkENDFILEis 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 -lis 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:
| Command | Time | Notes |
|---|---|---|
wc -l < big.txt | 0.13s | fastest raw line count |
awk 'END{print NR}' big.txt | 0.33s | about 2.5x slower here |
sed -n '$=' big.txt | 0.53s | workable, 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 -lfor 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
Print progress every million lines
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
BEGINFILEandENDFILE
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 safeFNRis safe on modern POSIX-style awk implementationsgawk ENDFILE count linesis a GNU awk feature and should be treated that way
Sources Checked
- GNU Awk User's Guide, built-in variables and record handling: https://www.gnu.org/software/gawk/manual/gawk.html
- GNU Awk User's Guide,
BEGINFILEandENDFILE: https://www.gnu.org/software/gawk/manual/gawk.html#BEGINFILE_002fENDFILE - POSIX
awkspecification, includingNR,FNR, andRS: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/awk.html - GNU coreutils manual for
wc: https://www.gnu.org/software/coreutils/manual/html_node/wc-invocation.html
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.
Related Guides and Tools
- Bash
wc -lcommand - sed
$=line counting - C and
wc -limplementation details - Python file I/O
- Vim line count guide
- Line Counter tool
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
16 min read
How to Count Lines in Bash: The Complete Guide with Edge Cases
Master line counting in Bash: count lines in files, variables, command output, and directories. Covers wc -l pitfalls, empty files, filenames with spaces, and shell script usage.
9 min read
How to Count Lines in sed (And Why $= Is Not the Same as $p)
Count lines in sed with sed -n '$=', understand the $= vs $p difference, handle multiple files, and avoid CRLF surprises. Covers GNU sed vs BSD sed, pattern-based counting, and when wc -l is the better tool.
13 min read
How to Count Lines in a File in C (And Why `fgetc` Is 9x Slower Than `fread`)
Count lines in a file in C — fgets, fread, mmap, and the large performance gap between them. Covers `wc -l` internals, Windows vs Linux portability, long-line traps, and production-ready counting patterns for large files.
20 min read
How to Count Lines in Python: 7 Methods, Benchmarked and Battle-Tested
Count lines in Python strings, text files, large files, and directories. Includes real performance benchmarks, empty file handling, splitlines vs split, and production-ready functions.