Table of Contents
Back to Blog

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.

POSIX sedGNU sedBSD sed
Published: May 14, 2026Updated: May 14, 20269 min readAuthor: Line Counter Editorial Team
sedUnixLinuxmacOSTutorial

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:

  • -n disables 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 content
  • sed -n '$=' file1.txt file2.txt is 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 file with sed -n '$='
  • sed count lines multiple files without the concatenation mistake
  • sed count lines matching pattern when you care about filtered rows
  • sed crlf line count behavior on Windows-style files
  • gnu sed vs bsd sed details 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 0 instead 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:

  • -n suppresses 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 number
  • sed -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: = or p

= 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:

  • -I keeps one continuous line-address space across files
  • -i treats 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 portable
  • sed -i syntax is not
  • -s is 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 -> about 0.13s
  • awk 'END{print NR}' big.txt -> about 0.31s
  • sed -n '$=' big.txt -> about 0.55s
  • sed -n '/123/p' big.txt | wc -l -> about 3.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 -l for 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 -c for line matches
  • use awk when 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

I also verified the core behavior locally with GNU sed 4.9 on Linux:

  • sed -n '$=' returns 3 for a three-line file
  • sed -n '$p' prints the last line content
  • sed -n '$=' file1.txt file2.txt returns one combined total
  • sed -n -s '$=' file1.txt file2.txt returns one count per file on GNU sed
  • sed -n '$=' returns 2 for a two-line file without a trailing newline
  • sed -n '$=' prints nothing for an empty file
  • CRLF files still count correctly with sed -n '$=', but sed '/^$/d' can miss blank Windows lines

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