Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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?

share

migrated from stackoverflow.com Mar 10 '12 at 15:05

This question came from our site for professional and enthusiast programmers.

1  
This is absolutely a programming question, and does not deserve to be migrated just because the answer involves the lowly shell programming language!!! –  tchrist Mar 9 '12 at 2:31
    
If you know in advance that they're named log1.gz through log164.gz, then what do you even need to ls -1 them for? –  ruakh Mar 9 '12 at 2:32
    
@ruakh ls -1 displays the results in one column rather then across –  Rabiani Mar 9 '12 at 2:40
1  
@Rabiani: I know what ls -1 does: it lists filenames. Since you already knew the filenames, I didn't understand what you needed it for. But since you've accepted Kevin's answer, I now know: you didn't need it. Which makes more sense. :-) –  ruakh Mar 9 '12 at 2:41

7 Answers 7

up vote 16 down vote accepted

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

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

Why not to use built-in ls feature for this particular case, namely

-v natural sort of (version) numbers within text

For example ls -1v log*

share

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

ls -v

In zsh:

echo *(on)

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 -l log?.gz; ls -l log??.gz; ls -l 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).

share

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.

share

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.

share

Perl solution:

ls log*.gz | perl -ne 'sub getnum{ $_[0] =~ /log(\d+)\.gz/; $1 }; push @A, $_; END{ print sort { getnum $a <=> $b } @A}'
share

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
share

This site is currently not accepting new answers.