Home / Alt manpages / nl(1)

  • nl(1)
  • User command
  • linux

Number Files Precisely with nl

You will finish with a repeatable way to add line numbers to a file, include or skip blank lines, and format the result for a review or 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 remain unnumbered. The number is right-justified in six columns and is 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 that 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 remains in place.

3. Choose whether blank lines receive 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, number 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 controls body lines; headers and footers have separate styles.

4. Make 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. The beginning of the output is therefore:

0100: alpha
0110: 
0120: beta

The -n formats are ln for left-justified numbers, rn for right-justified numbers without leading zeroes, and rz for right-justified numbers with leading zeroes. -s changes the separator, while -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 expected value.

5. Understand sections and resets

nl recognises logical page sections using delimiter lines. By default the delimiter is \: in the input, meaning a line containing two backslashes followed by a colon identifies a section boundary. The header, body and footer styles can 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 section. 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. First inspect the input if numbering appears to restart unexpectedly. You can change the delimiter with -d CC; an omitted second delimiter 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 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

If the output looks unchanged, check whether you selected -b n or a pattern that matches nothing. If blank lines are missing from the sequence, use -b a. If numbering restarts, inspect for section delimiters and try -p. If the 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 appropriate for a one-off inspection, but keep the output destination writable by your normal account. Never use a privileged redirection such as sudo nl file > file as an editing method: the shell opens the destination before sudo runs, and the command would still not provide an in-place update.

There is no undo operation because nl does not modify its input. If you created a temporary numbered file, remove that specific file after checking it, for example rm -- /tmp/README.numbered. Confirm the path before removing anything.

Done means

  • You confirmed the local GNU coreutils version.
  • You numbered a file or pipeline without changing the source.
  • You selected the correct body style for blank and matching lines.
  • You can control starting value, increment, width, format and separator.
  • You know when section resets apply and how -p changes them.