Home / Alt manpages / nl(1)

  • nl(1)
  • User command
  • linux

Number Lines Precisely with nl

Line numbers scrawled on by hand drift out of sync the moment someone edits the file, which is exactly the annoyance nl exists to remove. This guide gets you a repeatable way to add line numbers to a file, include or skip blank lines, and format the result for a review or a script. The examples use GNU nl from coreutils 9.4, installed on this machine. nl reads its input and writes numbered text, it does not edit the source file.

Allow about ten minutes. You need a shell and a readable text file. The normal examples need no elevated privileges. Do not use sudo unless the file permissions genuinely require it, and do not redirect output over the original file while experimenting.

1. Check the installed command

Confirm the version before relying on details in a script:

nl --version

Expected output begins with:

nl (GNU coreutils) 9.4

Version differences matter when you are comparing output from another host. The behaviour below is based on the local manual page and this installed command.

2. Number a file without changing it

Pass one or more files as arguments. With no file, or with -, nl reads standard input:

nl README.txt

The default is easy to miss: non-empty lines are numbered, while blank lines stay unnumbered. The number is right-justified in six columns and followed by a tab:

     1	First line

     2	Second line

     3	Third line

The visible gap before each number is padding, not part of the original file. Check the source was not altered by comparing its checksum before and after a run:

sha256sum README.txt
nl README.txt > /tmp/README.numbered
sha256sum README.txt

The two checksums for README.txt should match. The output file under /tmp is disposable, the source stays exactly where it was.

3. Choose whether blank lines get numbers

Use -b or --body-numbering with one of four styles. The most useful alternatives are t, the default, and a, every line:

nl -ba notes.txt

For a file containing alpha, a blank line, then beta, the result is:

     1	alpha
     2	
     3	beta
  • Use -b n to number no body lines, leaving the file text visible but removing body numbers.
  • Use -b pBRE to number only non-empty lines matching a basic regular expression, for example lines beginning with a capital letter:
nl -b 'p^[A-Z]' notes.txt

That expression is a basic regular expression, not a shell glob, quote it so the shell passes the pattern to nl unchanged. A common trap is assuming -b a means "all lines in every part of a logical page". It only controls body lines; headers and footers have separate styles.

4. Make the numbering suitable for a report

Several small options control the number sequence and its appearance:

nl -ba -v 100 -i 10 -w 4 -n rz -s ': ' notes.txt

This starts at 100, increases by 10, reserves four columns, pads with zeroes, and places a colon and space after each number, so the output begins:

0100: alpha
0110: 
0120: beta
  • -n ln left-justifies the numbers.
  • -n rn right-justifies them without leading zeroes.
  • -n rz right-justifies them with leading zeroes.
  • -s changes the separator, -w changes the number width.

If the sequence grows beyond the selected width, the number can occupy more columns, so choose a width that covers the largest value you expect.

5. Understand sections and resets

nl recognises logical page sections using delimiter lines. By default the delimiter is \: in the input, meaning a line with two backslashes followed by a colon marks a section boundary. The header, body and footer styles can each number the parts of that section:

nl -h a -b a -f a report.txt

Most ordinary text files do not contain these delimiters, so their section behaviour is usually invisible. When sections are present, the default numbering restarts at 1 for each one. Add -p or --no-renumber to keep the sequence moving across sections:

nl -p report.txt

Do not add section options simply to number a normal file. Inspect the input first if numbering appears to restart unexpectedly. You can change the delimiter with -d CC, an omitted second character implies a colon. The GNU extension -d '' disables section matching entirely.

6. Use standard input in a pipeline

A hyphen makes the input boundary explicit and lets another command produce the text:

printf '%s\n' 'ready' '' 'done' | nl -ba

Expected output is:

     1	ready
     2	
     3	done

For a script, preserve the exit status and avoid parsing padded columns unless the format is genuinely part of your interface. If another tool needs a stable delimiter, choose one deliberately, for example -s ': '. Do not treat line numbers as permanent identifiers, inserting or removing a line changes every number after it.

7. Recover from common mistakes

  • Output looks unchanged? Check whether you selected -b n or a pattern that matches nothing.
  • Blank lines missing from the sequence? Use -b a.
  • Numbering restarts partway through? Inspect for section delimiters and try -p.
  • Command says it cannot open a file? Check the path and permissions without changing them:
test -r notes.txt && echo 'readable' || echo 'not readable'

Reading a protected file with sudo nl /path/to/file may be fine for a one-off inspection, but keep the output destination writable by your normal account. Warning: never use a privileged redirection such as sudo nl file > file as an editing method. The shell opens the destination before sudo even runs, and the command still would not give you an in-place update.

There is no undo operation because nl never modifies its input. If you created a temporary numbered file, remove that specific file once you have checked it, for example rm -- /tmp/README.numbered, and confirm the path before removing anything.

Done means

  • Version confirmed: you checked the local GNU coreutils version.
  • Source untouched: you numbered a file or pipeline without changing the source.
  • Body style chosen: you selected the correct style for blank and matching lines.
  • Formatting controlled: you can set starting value, increment, width, format and separator.
  • Section resets understood: you know when they apply and how -p changes them.