How to Number Lines in Linux Terminal Using the nl Command

When reviewing source code, examining system logs, or preparing text files for documentation, having clear line numbers is invaluable for pinpointing specific references. While many Linux users rely on cat -n to prepend simple numbers to lines, the basic cat utility offers little flexibility: it numbers every single line sequentially, including blank spaces, without any options for formatting, justification, or numbering increments.

The standard Unix/Linux nl (Number Lines) utility provides a far more powerful and customisable solution. With nl, you can control line number formatting, choose whether to number empty lines, set custom delimiters, specify numbering increments, and even partition documents into distinct logical sections. In this guide, we explore how to master the nl command in the Linux terminal.

Why Use nl Instead of cat -n

While cat -n is convenient for quick inspections, it treats every byte sequence equally. In contrast, nl was intentionally designed as a text filter with advanced formatting capabilities:

  • Intelligent Blank Line Handling: By default, nl numbers only non-empty lines, keeping clean whitespace intact without cluttering empty rows with meaningless digits.
  • Custom Alignment: You can right-justify, left-justify, or pad numbers with leading zeros (e.g. 001, 002, 003).
  • Custom Separators: Replace default tab spacing with colons, arrows, or custom brackets between the line number and text content.
  • Logical Document Sections: Supports page headers, body sections, and footers with independent numbering logic.

Basic Syntax and Default Behaviour

The primary syntax of the nl command is as follows:

nl [options] [file...]

When executed without arguments, nl reads the target file and prints only non-empty lines with right-justified line numbers separated by a tab character:

nl script.sh

Notice that empty lines in the file remain completely blank without receiving a number. This clean presentation makes scanning code functions significantly easier.

Numbering Non-Empty vs All Lines (-b Option)

The -b (body numbering) flag controls which lines are assigned a number:

  • Number non-empty lines only (default):
    nl -b t filename.txt
    (The t stands for “translatable” non-empty lines.)
  • Number all lines including blanks:
    nl -b a filename.txt
    This behaves similarly to cat -n, numbering every line sequentially regardless of content.
  • Do not number any lines in the body:
    nl -b n filename.txt
    Suppresses numbering entirely for that section.
  • Number only lines matching a Regular Expression:
    nl -b p'^ERROR' application.log
    This command numbers only lines that begin with “ERROR”, leaving other lines unnumbered.

Setting Custom Delimiters and Separators (-s Option)

By default, nl places a tab character between the line number and the line’s content. If you are generating Markdown tables, CSV extracts, or formatted terminal outputs, you can define a custom delimiter using the -s option:

To separate numbers with a colon and a space:

nl -s ": " source_code.c

To separate numbers with an arrow:

nl -s " -> " notes.txt

Controlling Number Justification and Leading Zeros (-n Option)

The -n option governs the visual format and alignment of the line numbers:

  • Right-justified without leading zeros (default):
    nl -n rn document.txt
  • Left-justified without leading zeros:
    nl -n ln document.txt
    Aligns the digits against the far-left margin.
  • Right-justified with leading zeros:
    nl -n rz document.txt
    Produces numbers formatted like 000001, 000002, which is ideal for maintaining fixed-width columnar alignment in data pipelines.

Customising Field Width and Step Increments (-w and -i Options)

By default, nl reserves a field width of 6 characters for numbers. For shorter scripts or compact outputs, you can narrow this field width using -w:

nl -w 3 -n rz -s " | " checklist.txt

This produces output formatted cleanly as: 001 | Item one, 002 | Item two.

If you need line numbers to increment in steps of 5 or 10 (common in legacy programming language lines or test routines), use the -i (increment) flag:

nl -i 10 -s ": " basic_code.txt

The lines will be numbered 1, 11, 21, 31, and so forth.

Specifying Starting Line Numbers (-v Option)

When processing partitioned files or continuing line counts from an earlier segment, use the -v flag to define an arbitrary starting number:

nl -v 100 chapter2.txt

The first line of the file will begin with number 100.

Using nl in Shell Pipelines

Like all standard Unix text filters, nl reads from standard input when no file operand is supplied, allowing seamless integration into shell pipelines:

To number active listening network ports:

ss -tulpn | nl -w 2 -s ". "

To number and inspect unique logged user logins:

last | cut -d' ' -f1 | sort | uniq | nl -w 3 -s ") "

Get the best tech tips delivered straight to your inbox.

Join thousands of readers mastering Apple, Google, Microsoft, and Linux.