7

This is the standard output of ls -ln | nl

wolf@linux:~$ ls -lh | nl
     1  total 24
     2  -rw-rw-r-- 1 wolf wolf  186 Sep  24 22:18 01.py
     3  -rw-rw-r-- 1 wolf wolf  585 Sep  24 22:21 02.py
     4  -rw-rw-r-- 1 wolf wolf  933 Sep  24 22:26 03.py
wolf@linux:~$

Instead of starting the number from total 24, would it be possible to start it from the actual files/directory which is the second line?

Desired output

wolf@linux:~$ ls -lh | nl
        total 24
     1  -rw-rw-r-- 1 wolf wolf  186 Sep  24 22:18 01.py
     2  -rw-rw-r-- 1 wolf wolf  585 Sep  24 22:21 02.py
     3  -rw-rw-r-- 1 wolf wolf  933 Sep  24 22:26 03.py
wolf@linux:~$
0
15

Consume the first line with something else and pass the rest to nl

$ ls -lh | { sed -u q; nl; }
total 24
     1  -rw-rw-r-- 1 wolf wolf  186 Sep  24 22:18 01.py
     2  -rw-rw-r-- 1 wolf wolf  585 Sep  24 22:21 02.py
     3  -rw-rw-r-- 1 wolf wolf  933 Sep  24 22:26 03.py

sed (with -u to disable buffering), will consume one line and quit, implicitly printing the line before termination. nl consumes the rest of the lines.

If you really want the first line indented, you can do it like this:

$ ls -lh | { sed -u 's/^/        /;q'; nl; }
        total 24
     1  -rw-rw-r-- 1 wolf wolf  186 Sep  24 22:18 01.py
     2  -rw-rw-r-- 1 wolf wolf  585 Sep  24 22:21 02.py
     3  -rw-rw-r-- 1 wolf wolf  933 Sep  24 22:26 03.py

If you want to start the line numbering further down, tell sed when to quit:

$ ls -alh | { sed -u 's/^/        /;3q'; nl; }
        total 24
        drwx------ 1 wolf wolf  186 Sep  24 22:18 .
        drwx------ 1 wolf wolf  186 Sep  24 22:18 ..
     1  -rw-rw-r-- 1 wolf wolf  186 Sep  24 22:18 01.py
     2  -rw-rw-r-- 1 wolf wolf  585 Sep  24 22:21 02.py
     3  -rw-rw-r-- 1 wolf wolf  933 Sep  24 22:26 03.py

You could also use something like /\.\.$/q instead of 3q.

2
  • 4
    ls -lh | tail -n +2 | nl would probably be cleaner.
    – Ángel
    Sep 26 '20 at 0:04
  • 5
    @Ángel You lose the first line, though, and I think preserving it while not including it in the line numbering is the main point of this question.
    – JoL
    Sep 26 '20 at 0:43
13

This will make nl start from 0 :

$ ls -lh | nl -v 0
   0  total 24
   1  -rw-rw-r-- 1 wolf wolf  186 Sep  24 22:18 01.py
   2  -rw-rw-r-- 1 wolf wolf  585 Sep  24 22:21 02.py
   3  -rw-rw-r-- 1 wolf wolf  933 Sep  24 22:26 03.py
7

It's kind of ugly, but at least with the GNU Coreutils implementation of nl you can specify the lines to be numbered via a regular expression - since t is not one of the possible file types in the ls long-list output1 you could do

ls -lh | nl -bp'^[^t]'

Unfortunately this does not preserve the output alignment though.

Perhaps the most formally correct approach is to separate the ls output into distinct header and body sections:

$ ls -lh | sed -e '1s/.*/\\:\\:\\:\n&\n\\:\\:/' | nl

       total 0

     1  -rw-rw-rw- 1 steeldriver steeldriver   0 Sep  9 19:53 bar
     2  -rw-r--r-- 1 steeldriver steeldriver 449 Sep 16 17:41 bash_envs
     3  -rw-rw-rw- 1 steeldriver steeldriver 131 Sep  5 18:13 dirlist
     4  -rw-r--r-- 1 steeldriver steeldriver 359 Sep 13 19:42 error_status

(by default, header numbering style is none).

3
  • 2
    The alignment can be preserved if a sufficient number width is specified, e.g., ls -lh | nl -w4 -bp'^[^t]'.
    – Quasímodo
    Sep 25 '20 at 11:58
  • You'll still have a problem if some filenames or symlink targets contain \nt or nl's special delimiters. Sep 26 '20 at 13:10
  • @StéphaneChazelas: ls --hide-control-chars might be a good idea for this listing. Or one of the -Q quoting options to otherwise avoid printing a literal newline as part of the filename. Presumably the desired result with numbered lines is one file per line, so newlines in filenames would break that. (And/or not numbering "continuation" lines might be desired, if there was a reliable way to make that happen.) Sep 26 '20 at 19:23
6

You could try it with awk instead:

ls -lh | awk '{printf("%5s  ",NR==1?" ":NR-1); print}'

This will print the "totals" line without numbering it, and all remaining lines with numbers, right-adjusted, with a field width of 5. The space between number and line content will be two spaces (the text after the 5s in the printf() format string); you can replace it with something else such as \t.

If you don't need the "totals" line, you can use

ls -lh | awk 'NR>1{print (NR-1) "\t" $0}'

or

ls -lh | awk 'NR>1 {printf("%5s  ",NR-1); print}'

instead.

1
  • ... and once again, it would be nice to understand what is wrong with the answer that merits a down-vote ...
    – AdminBee
    Sep 28 '20 at 7:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.