Viewing files
nl
Print files with line numbers added, with control over which lines get numbered and how.
nl [OPTION...] [FILE...]
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
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
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
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
# 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
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
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
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 ') '
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
nldoes not number blank lines unless you pass-b a, so its numbers can disagree with an editor's line numbers or withgrep -n. Use-b a(orcat -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-dwith 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
nlsupports the same short options (-b,-n,-w,-s,-v,-i); long options such as--body-numberingare GNU only.
Options worth knowing
A selection, described in our own words. For the full list, see nl(1) or nl --help.
| Option | What it does |
|---|---|
-b, --body-numbering STYLE | Which lines to number: a (all), t (non-empty, the default), n (none) or pREGEX (matching lines). |
-n, --number-format FORMAT | Number layout: ln (left), rn (right, the default) or rz (right, zero-padded). |
-w, --number-width NUMBER | Width of the number field, 6 by default. |
-s, --number-separator STRING | Text placed after each number instead of a tab. |
-v, --starting-line-number NUMBER | First line number. |
-i, --line-increment NUMBER | Step between line numbers. |
-l, --join-blank-lines NUMBER | Count a group of NUMBER blank lines as one (with -b a). |
-d, --section-delimiter CC | Use CC instead of \: to mark logical page sections. |
-p, --no-renumber | Do not reset numbering at section boundaries. |
-h, --header-numbering STYLE | Numbering style for header sections. |
-f, --footer-numbering STYLE | Numbering style for footer sections. |