223

I have a bunch of files from log1 to log164.

I'm trying to LIST the directory (sorted) in a UNIX terminal but the sort functions are only providing the format like this:

home:logs Home$ ls -1 | sort
log1.gz
log10.gz
log100.gz
log101.gz
log102.gz
log103.gz
log104.gz
log105.gz
log106.gz
...etc

What I want is

home:logs Home$ ls -1 | sort
log1.gz
log2.gz
log3.gz
log4.gz
log5.gz
log6.gz
log7.gz
...{more here}
log99.gz
log100.gz
log101.gz
log102.gz
...etc

Any suggestions in what I could use to do this?

0

11 Answers 11

430

Why not use the built-in GNU ls feature for this particular case:

-v — natural sort of (version) numbers within text

For example:

ls -1v log*
5
  • 30
    On BSD/OSX this option is something else: -v - Force unedited printing of non-graphic characters.
    – kenorb
    Commented Feb 26, 2015 at 17:33
  • Unfortunately the -v option is not available on AIX (6.1)
    – bouvierr
    Commented Oct 17, 2016 at 15:40
  • 13
    for MacOS this will work, couldn't find an option as mentioned, only ls | sort -n
    – Ricky Levi
    Commented Aug 25, 2018 at 12:31
  • 3
    Don't work on MacOS.
    – mrgloom
    Commented Apr 17, 2019 at 13:42
  • 4
    To make this work on MacOS. Install coreutils: brew install coreutils and add them to path PATH="/usr/local/opt/coreutils/libexec/gnubin:$PATH" (or just use g-prefix: gls -lv)
    – zbstof
    Commented Feb 27, 2020 at 13:32
69

With GNU ls (i.e. on Linux, Cygwin, or other systems that have GNU ls specifically installed):

ls -v

In zsh:

echo *(n)

In other shells:

echo log?.gz log??.gz log???.gz

Replace echo by printf '%s\n' if you want each file name on a separate line.

If you want file metadata as well (ls -l) and you don't have GNU ls, you'll need to call ls separately for each file name or group of file names that you want to see in lexicographic order.

ls -ld log?.gz; ls -ld log??.gz; ls -ld log???.gz

To avoid these difficulties, use enough leading zeroes in your file names so that the lexicographic sort is human-friendly (log001.gz, etc).

2
  • I'm getting bitten by this more often that I'd like to admit; is there a way, in ZSH, to default globbing to (n)?
    – Michaël
    Commented Feb 1, 2021 at 18:49
  • 1
    @Michaël setopt numeric_glob_sort Commented Feb 1, 2021 at 21:38
54

bash's braces, {}, will enumerate them in order:

for file in log{1..164}.gz; do
    process "$file"
done
40

The GNU sort (as available on Linux,) has a "version sort" mode that interprets numberes inside non-numbers just the way you ask for:

From man 1 sort:

    -V, --version-sort
           natural sort of (version) numbers within text

(Creating empty test files to list:
touch log1.gz log2.gz log3.gz log99.gz log100.gz log101.gz log102.gz)

Your example case, adding the -V option (or --version-sort):

ls -1 log*.gz | sort -V
log1.gz
log2.gz
log3.gz
log99.gz
log100.gz
log101.gz
log102.gz
37

While the solution ls -1v is certainly the nicest in this particular case, I think it's good to have also one that works with sort as in the original question, since this works also when your input does not come from ls. In this case you can use:

ls -1 | sort -n -k1.4

The -n option tells sort to sort numerically, and -k 1.4 sets the sort key to the first field (which is the whole filename in this case) starting from the 4th character up to the last.

2
  • In my case, ls -1 | sort -n -k1.4 does not work. It gives unsorted ones at first up to 4 chars then sorted ones after 4th character. I used ls -1 |sort | sort -n -k1.4 instead and it worked perfectly.
    – Prabhu
    Commented Sep 12, 2014 at 20:53
  • 3
    @Prabhu, instead, you could do sort -k1.1,1.3 -k1.4n. sort implementations are not required to be stable so your approach won't work with all implementations. See also the -V option of GNU and FreeBSD sort. Commented May 26, 2015 at 11:11
11

if you use Mac or BSD try this:

ls -1 *.jpg | sort -n
2
  • 2
    -n didn't work for me on OSX, I needed -V Commented Feb 23, 2021 at 23:58
  • Worked beautifully!
    – zero_cool
    Commented Nov 2, 2022 at 22:56
3

My version of Solaris doesn't support ls -v (grrr). And the sort solution provided above 1) requires knowledge of the position of digits in the filename, and 2) doesn't handle things like multi-part version numbers.

The approach below is Solaris-compatible, doesn't require foreknowledge of the digit positions, and handles version numbers with 2, 3 or 4 components (like: a-1.2, foo-5.6.7, bar_baz_9.10.11.12). It also uses sort -f to fold upper- and lowercase together, and properly handles directories intermixed with files:

ls -d | sort -f -t . -k 1,1 -k 2,2n -k 3,3n -k 4,4n

Note that this version limits the first component to a single digit.

If your target operating system(s) supports ls -v, that is clearly the superior solution.

2

Perl solution:

ls log*.gz | perl -ne 'sub getnum{ $_[0] =~ /log(\d+)\.gz/; $1 }; push @A, $_; END{ print sort { getnum $a <=> $b } @A}'
1
$ ls
log101.gz  log102.gz  log103.gz  log104.gz  log105.gz  log106.gz  log10.gz  log1.gz
$ ls | sort -t . -n -k1.4
log1.gz
log10.gz
log101.gz
log102.gz
log103.gz
log104.gz
log105.gz
log106.gz
1
0

This worked for me.

I have files 1.jpg 2.jpg ... 18.jpg

$ echo *.jpg | tr -s ' ' '\n' | sort -n

sort is getting confused with ls output because of unprintable colour characters. If you try this:

ls -1 --color=none *.jpg | sort -n

it will work perfectly.

sort can ignore unprintable characters with -i option but it still doesn't work and I don't know why.

But you can always strip colour like this and sort will work:

ls -1 --color=always *.jpg | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" | sort -n

I hope one day sort will have an option for this.

-1

Command:

ls -l | sort --human-numeric-sort
1
  • 1
    Please use proper syntax & explain what your command should yield
    – mattia.b89
    Commented May 1, 2020 at 20:21

You must log in to answer this question.