How to iterate through a list of files with spaces that is sorted (case insensitive)?

I am doing this in OSX which uses bash but obviously, not all bash conventions are used so hopefully your suggestion is available to me 🙂

I have the following files

  1. fun bar1.txt
  2. Foo bar2.tXT (that’s an uppercase XT)
  3. fun fun.txt

What I was is to iterate through the file list in a sorted manner?

something like:

for i in (my-sorted-file-list); do
    echo $i
done

Any ideas how this can be done? thanks a lot

Asked By: mrjayviper

||

If none contain newlines, then this will get you a sorted list:

_state=$(set +o)
set -f
IFS='
' ; for f in $(set +f; printf %s\n * |sort) ; do echo "$f" ; done
eval "$_state"

That is, it will get you a sorted list of all of the files in the current directory – here represented by *.

Answered By: mikeserv

Very simple:

for i in *; do
  echo "<$i>"
done

This uses bash’s file globbing. A sort is not necessary as bash already sorts pathname expansions.
From man bash:

   Pathname Expansion
       After word splitting, unless the -f option has been set, bash scans each word for
       the characters *, ?, and [. If one of these characters appears, then the word is
       regarded as a pattern, and replaced with an alphabetically sorted list of file
       names matching the pattern.

 

Example result:

$ touch 'fun bar1.txt' 'Foo bar2.tXT' 'fun fun.txt'

$ for i in *; do echo "<$i>"; done
<Foo bar2.tXT>
<fun bar1.txt>
<fun fun.txt>

Note that the sort order is dependent upon LC_COLLATE (just like the sort utility). If you want a case insensitive sort, use LC_COLLATE=en_US.utf8. If you want a case sensitive sort, use LC_COLLATE=C.

Also man bash:

   LC_COLLATE
          This variable determines the collation order used when sorting the results
          of pathname expansion, and determines the behavior of range expressions,
          equivalence classes, and collating sequences within pathname expansion and
          pattern matching.
Answered By: phemmer

One thing to bear in mind: in

for i in *.{txt,py,c}; do ...

The *.{txt,py,c} is first expanded to *.txt *.py *.c.

While *.txt is sorted, *.{txt,py,c} is not. It also means that if there’s not .py file, you will loop over the literal string *.py.

If you want a sorted global list, you should use an alternation globbing operator instead of brace expansion. With bash:

shopt -s extglob
for i in *.@(txt|py|c); do...

Whether the sorting will be done case sensitively or not is locale specific. To force case insensitive sorting in the current locale, with zsh, you can define a new sorting order like:

ci() REPLY=${(L)REPLY}
for i (*.(txt|py|c))(N.o+ci)) {...}
Answered By: Stéphane Chazelas
Categories: Answers Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.