opscape

Viewing files

nl

Print files with line numbers added, with control over which lines get numbered and how.

nl [OPTION...] [FILE...]

8 of 8 examples run in Debian 13.7 on 2026-10-31 · coreutils 9.7-3 · manual: nl(1)

nl copies its input to standard output with a line number in front of lines. Unlike cat -n, its default is to number only non-empty lines (-b t), and nearly everything about the numbers is adjustable: which lines get them (-b a all, -b t non-empty, -b pREGEX only lines matching a regex), the width, the separator, the start value and the step.

Reach for nl when the numbering itself matters, for instance to number only the real settings in a config file or to produce 1.-style lists. For a quick numbered view of a file, cat -n or grep -n '' are just as good.

Examples

Number the lines of a file

nl todo.txt
explain
output
     1	buy milk
     2	fix login bug
     3	write tests
     4	fix logout bug
     5	deploy v2.4

Each line gets a number right-aligned in a 6-character field, followed by a tab.

Blank lines are skipped by default

nl data/settings.conf
explain
output
     1	# demo service settings
     2	host = 0.0.0.0
     3	port = 8080
       
     4	# logging
     5	log_level = info
     6	log_file = /var/log/demo/app.log
     7	# max_connections = 100
     8	timeout = 30

The empty line after port = 8080 gets no number, and the count continues at 4 on the next line. cat -n would number it.

Number every line, including blank ones

nl -b a data/settings.conf
explain
output
     1	# demo service settings
     2	host = 0.0.0.0
     3	port = 8080
     4	
     5	# logging
     6	log_level = info
     7	log_file = /var/log/demo/app.log
     8	# max_connections = 100
     9	timeout = 30

-b a (body numbering: all) numbers blank lines too, matching what cat -n does.

Number only lines that match a pattern

nl -b 'p^[^#]' data/settings.conf
explain
output
       # demo service settings
     1	host = 0.0.0.0
     2	port = 8080
       
       # logging
     3	log_level = info
     4	log_file = /var/log/demo/app.log
       # max_connections = 100
     5	timeout = 30

-b p followed by a basic regex numbers only matching lines: here lines that start with something other than #, so the comments and the blank line are left unnumbered and the five real settings are counted 1 to 5.

Narrow numbers with a custom separator

nl -w 2 -s '. ' todo.txt
explain
output
 1. buy milk
 2. fix login bug
 3. write tests
 4. fix logout bug
 5. deploy v2.4

-w 2 makes the number field two characters wide and -s '. ' replaces the tab, giving a plain numbered list.

Zero-padded numbers

nl -n rz -w 3 todo.txt
explain
output
001	buy milk
002	fix login bug
003	write tests
004	fix logout bug
005	deploy v2.4

-n rz right-aligns the number and pads it with zeros; with -w 3 the first line is 001.

Start at another number and count in steps

nl -v 10 -i 10 todo.txt
explain
output
    10	buy milk
    20	fix login bug
    30	write tests
    40	fix logout bug
    50	deploy v2.4

-v 10 sets the first number and -i 10 the increment, so lines are numbered 10, 20, 30 and so on, like old BASIC listings.

Number the output of another command

grep -h ERROR logs/*.log | nl -w 1 -s ') '
explain
output
1) 2026-03-14 08:16:40 ERROR health check failed: database timeout after 5000ms
2) 2026-03-14 08:30:00 ERROR disk usage on /var at 91%

Without a file name nl reads standard input, so it numbers whatever the pipeline produces; here the two ERROR lines.

Common pitfalls

  • nl does not number blank lines unless you pass -b a, so its numbers can disagree with an editor's line numbers or with grep -n. Use -b a (or cat -n) when the numbers must match the file.

  • A line consisting only of \:, \:\: or \:\:\: is treated as a section delimiter and resets the numbering. It rarely happens by accident, but if numbers restart unexpectedly, look for such lines or pass -d with a different delimiter.

GNU vs BSD and macOS

The examples above ran the GNU/Linux version. These are the differences that matter on macOS and the BSDs (written from their documentation, not run here).

  • BSD/macOS nl supports the same short options (-b, -n, -w, -s, -v, -i); long options such as --body-numbering are GNU only.

Options worth knowing

A selection, described in our own words. For the full list, see nl(1) or nl --help.

OptionWhat it does
-b, --body-numbering STYLEWhich lines to number: a (all), t (non-empty, the default), n (none) or pREGEX (matching lines).
-n, --number-format FORMATNumber layout: ln (left), rn (right, the default) or rz (right, zero-padded).
-w, --number-width NUMBERWidth of the number field, 6 by default.
-s, --number-separator STRINGText placed after each number instead of a tab.
-v, --starting-line-number NUMBERFirst line number.
-i, --line-increment NUMBERStep between line numbers.
-l, --join-blank-lines NUMBERCount a group of NUMBER blank lines as one (with -b a).
-d, --section-delimiter CCUse CC instead of \: to mark logical page sections.
-p, --no-renumberDo not reset numbering at section boundaries.
-h, --header-numbering STYLENumbering style for header sections.
-f, --footer-numbering STYLENumbering style for footer sections.

Related