Table of Contents
sed Guide
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.
If you want the shortest honest answer to sed count lines, it is this:
sed -n '$=' file.txt
That one-liner is famous for a reason. It is concise, portable, and easy to remember once you know what it means:
-ndisables sed's normal line-by-line printing$is the address for the last input line=prints the current line number
So sed -n '$=' means "print the line number of the last line." That is why it works as sed count lines in file.
The two traps come immediately after that:
sed -n '$p'is not a line counter; it prints the last line's contentsed -n '$=' file1.txt file2.txtis not a per-file counter; by default sed reads multiple files as one continuous stream
There is also a platform detail that matters less than many tutorials imply. For sed count lines mac, the core command is the same. sed -n '$=' works on GNU sed and BSD/macOS sed. The bigger gnu sed vs bsd sed differences show up around option syntax like -i and GNU-only features like -s.
This guide keeps those jobs separate:
sed count lines in filewithsed -n '$='sed count lines multiple fileswithout the concatenation mistakesed count lines matching patternwhen you care about filtered rowssed crlf line countbehavior on Windows-style filesgnu sed vs bsd seddetails that actually affect line-count workflows
30-Second Cheat Sheet
- Count total lines in one file:
sed -n '$=' file.txt - Count total lines from stdin:
some_command | sed -n '$=' - Get
0instead of empty output for an empty file:count=$(sed -n '$=' file.txt); printf '%s\n' "${count:-0}" - Count each file separately in a portable shell loop:
for f in *.txt; do printf '%s: %s\n' "$f" "$(sed -n '$=' "$f")"; done - Count each file separately with GNU sed:
sed -n -s '$=' file1.txt file2.txt - Count matching lines:
sed -n '/ERROR/p' app.log | wc -l - Count nonblank lines:
sed '/^[[:space:]]*$/d' file.txt | wc -l - Fastest plain total:
wc -l < file.txt
If your whole job is sed count lines in file, stop at the first bullet. The rest of this article is for the edge cases that make sed count lines less obvious than it looks.
Method 1: sed -n '$='
This is the canonical sed count lines command:
sed -n '$=' file.txt
It is also the exact pattern used in GNU sed's own "Counting Lines" documentation.
Why it works:
-nsuppresses normal output$selects the last input line=prints that line's current line number
That means sed -n '$=' does not count with a loop in your shell script. It lets sed read the whole input stream and print only the last line number.
You can use the same pattern with standard input:
cat file.txt | sed -n '$='
printf 'a\nb\nc\n' | sed -n '$='
That makes sed count lines in file work equally well for files, pipes, generated output, and here-doc input.
A useful edge case: missing trailing newline
Unlike wc -l, sed -n '$=' still reports the logical line total when the file does not end with a newline.
Example:
printf 'a\nb' > no-final-newline.txt
sed -n '$=' no-final-newline.txt
wc -l < no-final-newline.txt
On this machine, sed returned 2 and wc -l returned 1.
That is a practical reason to prefer sed -n '$=' when you want the total number of text lines rather than the number of newline characters.
Empty files print nothing
There is one gotcha:
touch empty.txt
sed -n '$=' empty.txt
The output is empty, not 0.
If you are writing a shell script, normalize that explicitly:
count=$(sed -n '$=' empty.txt)
printf '%s\n' "${count:-0}"
That small shell fallback turns sed count lines into a script-safe pattern.
Method 2: $= vs $p
This is the one-character mistake that breaks a lot of sed count lines in file snippets:
sed -n '$=' file.txt
sed -n '$p' file.txt
They look nearly identical, but they do completely different things.
sed -n '$='prints the last line numbersed -n '$p'prints the last line content
Example:
printf 'first line\nsecond line\nthird line\n' > test.txt
sed -n '$=' test.txt
sed -n '$p' test.txt
Output:
3
third line
This is the clean mental model:
- address first:
$ - command second:
=orp
= is about line numbers. p is about line content.
If you are debugging sed addresses, these related commands help:
sed -n '=' file.txt
sed '=' file.txt | sed 'N; s/\n/\t/'
sed -n '10=' file.txt
But for sed count lines, the only one you really need to memorize is sed -n '$='.
Method 3: Multiple Files and the Concatenation Trap
This is the biggest sed count lines multiple files surprise:
sed -n '$=' file1.txt file2.txt
That does not print one count for each file. By default, sed treats multiple input files as one long stream.
In local testing:
printf 'a\nb\nc\n' > file1.txt
printf 'x\ny\nz\nw\n' > file2.txt
sed -n '$=' file1.txt file2.txt
The output was:
7
That is exactly how POSIX and GNU sed describe multi-file processing. $ matches the last line of the whole input stream unless the sed implementation or options tell it to separate files.
Portable per-file counting
If you want a real sed count lines multiple files solution, the portable shell answer is still the simplest:
for f in file1.txt file2.txt; do
printf '%s: %s\n' "$f" "$(sed -n '$=' "$f")"
done
Output:
file1.txt: 3
file2.txt: 4
GNU sed shortcut: -s
GNU sed has a useful option here:
sed -n -s '$=' file1.txt file2.txt
On this machine, that printed:
3
4
-s tells GNU sed to treat files as separate streams rather than one continuous stream. It is convenient, but it is not the portable answer for generic sed count lines mac or POSIX sed scripts.
So the rule is simple:
- one file:
sed -n '$=' - multiple files, portable: loop over files
- multiple files, GNU-only:
sed -n -s '$='
Method 4: GNU sed vs BSD sed
For line counting itself, gnu sed vs bsd sed is less dramatic than many blog posts imply.
This works on both:
sed -n '$=' file.txt
So sed count lines mac does not need a special counting syntax. The core sed count lines in file command is already portable.
The real differences show up in nearby workflow details.
-i syntax is different
GNU sed accepts in-place editing like this:
sed -i 's/old/new/' file.txt
BSD/macOS sed expects a backup suffix argument, even when it is empty:
sed -i '' 's/old/new/' file.txt
That difference matters a lot for editing workflows, but not for sed -n '$='.
Some BSD-family sed variants add one more wrinkle. FreeBSD documents both -I and -i:
-Ikeeps one continuous line-address space across files-itreats each file independently
That is a BSD-family in-place-editing detail, not something you need for normal sed count lines in file usage. But it explains why multi-file address behavior can look different once in-place editing options enter the picture.
-E is the portable extended-regex spelling
If you need extended regular expressions in the same script, prefer:
sed -E 's/[0-9]+/NUM/' file.txt
That spelling is the safer cross-platform choice. GNU sed also documents other extensions, but -E is the better default if you want one script to behave on Linux and macOS.
-s is a GNU sed convenience
This is the line-counting difference that actually matters:
sed -n -s '$=' file1.txt file2.txt
That is handy on GNU sed, but it is not the portable baseline. If you want sed count lines multiple files in a script that may run on BSD/macOS sed, use the shell loop from the previous section.
So the short gnu sed vs bsd sed summary is:
sed -n '$='is portablesed -isyntax is not-sis a GNU sed convenience, not a portable sed assumption
Method 5: CRLF Files and Blank-Line Filters
This is where sed crlf line count becomes more subtle.
For total line count, CRLF is not the problem many people think it is.
If a Windows file uses \r\n, this still works:
sed -n '$=' windows.txt
In local testing with a CRLF file containing alpha, a blank line, and beta, sed returned 3, which is correct.
The real trap is blank-line filtering.
Why ^$ fails on CRLF files
This classic Unix blank-line filter:
sed '/^$/d' windows.txt | wc -l
can give the wrong answer on CRLF input, because a Windows "empty" line often contains a carriage return character before the line feed. So the line is not truly empty from sed's point of view.
On this machine, a CRLF sample file with one blank line produced 3 instead of the expected 2 with that command.
Better options
If you want nonblank lines, these are safer:
sed '/^[[:space:]]*$/d' windows.txt | wc -l
tr -d '\r' < windows.txt | sed '/^$/d' | wc -l
The first form treats carriage return as whitespace. The second normalizes line endings first.
That distinction matters because sed count lines and sed count lines matching pattern are not the same as "count lines after a blank-line cleanup pass." CRLF makes that cleanup step the fragile part, not the main sed -n '$=' count.
Method 6: Count Matching, Nonblank, and Ranged Lines
Pure total count is only one use case. A lot of real sed count lines tasks are filtered counts.
Count matching lines
The classic sed pattern is:
sed -n '/ERROR/p' app.log | wc -l
That is a valid sed count lines matching pattern pipeline because sed prints only matching lines and wc -l counts them.
If all you need is the count, though, grep -c is usually the better tool:
grep -c 'ERROR' app.log
It is shorter, and in practice it is usually faster than sed ... | wc -l.
Count nonblank lines
Use this when blank or whitespace-only lines should not count:
sed '/^[[:space:]]*$/d' file.txt | wc -l
This is better than sed '/^$/d' when sed crlf line count behavior matters.
Count a specific range
You can also count a slice of the file:
sed -n '10,50p' file.txt | wc -l
That returns 41, because both endpoints are included.
Count comment lines
sed -n '/^[[:space:]]*#/p' config.ini | wc -l
Count nonblank, non-comment lines
sed '/^[[:space:]]*$/d; /^[[:space:]]*#/d' source.py | wc -l
This is the point where sed count lines matching pattern turns into small text-processing workflows rather than one-line totals.
Method 7: sed vs wc -l
GNU sed documents sed -n '$=' as the classic line-count pattern, but that does not make sed the fastest choice.
For raw totals, wc -l is usually faster.
On this Linux machine, using GNU sed 4.9 and a cached synthetic file of about 110 MB with 10,000,000 lines, local timings looked like this:
wc -l < big.txt-> about0.13sawk 'END{print NR}' big.txt-> about0.31ssed -n '$=' big.txt-> about0.55ssed -n '/123/p' big.txt | wc -l-> about3.50s
These are local measurements, not universal rules. Cache state, storage speed, sed implementation, and input shape all matter.
Still, the practical ranking is stable:
- use
wc -lfor the fastest raw total - use
sed -n '$='when you want a compact sed-native total and correct handling of a missing trailing newline - use
grep -cfor line matches - use
awkwhen line counting is only one part of a more complex processing pass
That is the real answer to sed count lines in production work. The best command depends on whether you are counting all lines, matching lines, or per-file totals across many inputs.
Sources Checked
- GNU sed manual, "Counting Lines": https://www.gnu.org/software/sed/manual/html_node/wc-_002dl.html
- GNU sed manual, "Addresses": https://www.gnu.org/software/sed/manual/html_node/Addresses.html
- GNU sed manual, "Command-Line Options": https://www.gnu.org/software/sed/manual/html_node/Command_002dLine-Options.html
- POSIX
sedspecification: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html - FreeBSD
sed(1)manual page: https://man.freebsd.org/cgi/man.cgi?query=sed&sektion=1 - macOS
sed(1)manual page: https://man.freebsd.org/cgi/man.cgi?query=sed&sektion=1&manpath=macOS+10.13.6
I also verified the core behavior locally with GNU sed 4.9 on Linux:
sed -n '$='returns3for a three-line filesed -n '$p'prints the last line contentsed -n '$=' file1.txt file2.txtreturns one combined totalsed -n -s '$=' file1.txt file2.txtreturns one count per file on GNU sedsed -n '$='returns2for a two-line file without a trailing newlinesed -n '$='prints nothing for an empty file- CRLF files still count correctly with
sed -n '$=', butsed '/^$/d'can miss blank Windows lines
Related Guides and Tools
- AWK
NRandFNRline counting - Bash
wc -lcommand - grep
-cline counting - Python file I/O
- Vim line counting
- Line Counter tool
Need to count lines without remembering sed address syntax?
Try the Line Counter. No $= versus $p confusion, no multi-file sed traps, just the number.
Frequently Asked Questions
How do I count lines in sed?
Use sed -n '$=' file.txt. That prints the last line number, which is the total line count for the input stream.
What is the difference between sed -n '$=' and sed -n '$p'?
sed -n '$=' prints the last line number. sed -n '$p' prints the last line content. One counts lines, the other shows text.
Does sed count lines correctly without a trailing newline?
Yes. sed -n '$=' reports the logical last line number, so it still returns 2 for a file containing two lines where the final line has no terminating newline.
How do I count lines in multiple files with sed?
By default, sed treats multiple files as one continuous stream, so sed -n '$=' file1 file2 prints one combined total. For per-file counts, loop over files or use GNU sed -s.
Does sed -n '$=' work on macOS?
Yes. sed count lines mac uses the same sed -n '$=' pattern. The larger GNU sed vs BSD sed differences show up around options like -i, not this counting form.
How do I count matching lines with sed?
Use sed -n '/pattern/p' file.txt | wc -l, but for pure matching-line counts grep -c is usually simpler and faster.
Related Guides
10 min read
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.
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 with grep (-c Flag, Total Lines, and the Multi-File Advantage)
Count lines with grep using -c for matches, grep -c '' for total lines, and grep -vc for non-matching lines. Covers multi-file per-file counts, GNU grep vs BSD grep compatibility, missing trailing newline behavior, and grep vs wc -l tradeoffs.
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.