The nl command is a command line text formatting utility in Linux. The command is one of the hidden gems in Linux as only the most seasoned admins are familiar with it. As with most Linux commands it's name derives from it's function, number lines. It's main purpose is to display line numbers of a file or standard input.

Write each FILE to standard output, with line numbers added. With no FILE, or when FILE is -, read standard input.

-Excerpt from nl man page

In this tutorial we will show you both the basic usage of the nl command and some more advanced techniques with examples.

Basic Usage

Passing a Filename as an Argument

In the most basic usage of nl you pass a filename as an argument. The utility then takes the file and prints it to standard out and numbers each line.

[savona@putor tmp]$ nl /etc/passwd
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
6 sync:x:5:0:sync:/sbin:/bin/sync
...OUTPUT TRUNCATED...

Here it took the /etc/passwd file and printed it to the screen with line numbers added to each line.

Formatting the Output

There are many ways to format the output of the nl command. For example, it would be common to see a colon after the number in a ordered list. You can use the -s option to add any string you want after the line number.

[savona@putor tmp]$ nl -s: /etc/passwd
1:root:x:0:0:root:/root:/bin/bash
2:bin:x:1:1:bin:/bin:/sbin/nologin
3:daemon:x:2:2:daemon:/sbin:/sbin/nologin
4:adm:x:3:4:adm:/var/adm:/sbin/nologin
5:lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
6:sync:x:5:0:sync:/sbin:/bin/sync
...OUTPUT TRUNCATED...

You can add a space after the colon (or any string) by using double quotes around a space like so:

[savona@putor tmp]$ nl -s:" " /etc/passwd
1: root:x:0:0:root:/root:/bin/bash
2: bin:x:1:1:bin:/bin:/sbin/nologin
3: daemon:x:2:2:daemon:/sbin:/sbin/nologin
4: adm:x:3:4:adm:/var/adm:/sbin/nologin
5: lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
6: sync:x:5:0:sync:/sbin:/bin/sync
..OUTPUT TRUNCATED...

The default numbering format is right justified, no leading zeros ( rn ). You can change the numbering format by using the -n option followed by your format of choice.

Left Justified, No Leading Zeros ( ln )

[savona@putor tmp]$ nl -n ln /etc/passwd
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
6 sync:x:5:0:sync:/sbin:/bin/sync
...OUTPUT TRUNCATED...

Right Justified, Leading Zeros ( rz )

[savona@putor tmp]$ nl -n rz /etc/passwd
000001 root:x:0:0:root:/root:/bin/bash
000002 bin:x:1:1:bin:/bin:/sbin/nologin
000003 daemon:x:2:2:daemon:/sbin:/sbin/nologin
000004 adm:x:3:4:adm:/var/adm:/sbin/nologin
000005 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
000006 sync:x:5:0:sync:/sbin:/bin/sync
...OUTPUT TRUNCATED...

The default style is right justified, no leading zeros, but it displays the same output as passing no style.

Advanced Options

Using Styles ( -b )

There are three output styles to choose from:

  • a = Number all lines
  • t = Number only nonempty lines
  • n = Number no Lines
  • p = Number only lines that contain a match for the basic regular expression

By default the nl command uses the ( t ) style and numbers only nonempty lines.
I have yet to figure out why they included the ( n ) style which numbers no lines.

The syntax for adding one of the styles is:

nl -b style filename

Numbering All Lines ( a )

[savona@putor tmp]$ nl -ba test 
1 This is a test file
2
3
4 Above are some blank lines
5
6
7 Maybe they will will be numbered, maybe not.
8 This is the end. Beautiful friend, the end.

Numbering Lines that Match a Regular Expression

Let's say we wanted to only number the lines for users with a UID and GUID of 0 through 2. We can use regular expression matching like so:

[savona@putor tmp]$ nl -b p":[0-2]:[0-2]" /etc/passwd
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
...OUTPUT TRUNCATED...

Changing the Starting Line Number

You can change the starting line number by using the -v option followed by the number you wish to start with.

[savona@putor tmp]$ nl -v 77 /etc/passwd
77 root:x:0:0:root:/root:/bin/bash
78 bin:x:1:1:bin:/bin:/sbin/nologin
79 daemon:x:2:2:daemon:/sbin:/sbin/nologin
80 adm:x:3:4:adm:/var/adm:/sbin/nologin
81 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
82 sync:x:5:0:sync:/sbin:/bin/sync
...OUTPUT TRUNCATED...

Dealing with Empty Lines

You can make X number of consecutive empty lines count as a logical line and only number the last one. For example, let's assume I wanted to make every 2 consecutive empty lines count as one logical line and number the last one.

[savona@putor tmp]$ nl -ba -l2 test 
1 This is a test file

2

3

4 Above are some blank lines

5
6 Maybe they will will be numbered, maybe not.
7 This is the end. Beautiful friend, the end.

As you can see, it only numbered every second consecutive blank line.

NOTE: You have to use this with -ba to number all lines because by default nl does not number blank lines.

Using nl in a Pipeline

You can use nl command in a pipeline to add line numbers to output you normally wouldn't be able to. For example, if we wanted a numbered list of directories inside the home directory.

[savona@putor tmp]$ ls /home/ | nl -s:
1:lost+found
2:new
3:savona
4:testuser

Using a Delimiter

In order to use a delimiter in the nl command, the file must be written in logical pages. The default delimiter is backslash colon ( \: ). Each logical page MUST have the following three sections:

  • Header - Signified by three consecutive delimiters, by default \:\:\:
  • Body - Signified by two consecutive delimiters, by default \:\:
  • Footer - Signified by a single delimiter, by default \:

The delimiter must be two characters, for example, let's change the delimiter to two equal signs ( == ).

Here is our formatted input file:

[savona@putor tmp]$ cat test 
======
Page 1
====
This is a test file
Above are some blank lines
Maybe they will will be numbered, maybe not.
This is the end. Beautiful friend, the end.
=
======
Page 2
====
Hey you, out there in the cold.
Getting lonely, getting old.
=

As you can see each logical page has the three sections. The header which consists of three double character delimiters (======), a head with consists of two double character delimiter (====), and a footer with consists of a single double character delimiter (==).

Now we use the nl command with the -d option to number the lines.

[savona@putor tmp]$ nl -d== test 
Page 1
1 This is a test file

2 Above are some blank lines
3 Maybe they will will be numbered, maybe not.
4 This is the end. Beautiful friend, the end.

Page 2
1 Hey you, out there in the cold.
2 Getting lonely, getting old.

I honestly don't know why anyone would go through the trouble of doing this, but I thought I would show it to you for completeness.

Conclusion

The nl command has a useful function, although in my opinion a lot of it's options do not add much value in day to day use. But having another tool in your Linux toolbox, never hurts.