1

Is it possible in MacOS Terminal to have the sort order numerically and case-insensitively, almost like in Finder? I'm on MacOS 14 Sonoma, using Terminal app with zsh 5.9 and oh-my-zsh.

I'm trying to execute some command line for all files in a specific folder. Please compare:

My desired sort order MacOS Finder sort by name order
bar.jpg
bar-2.jpg
bar-10.jpg
foo.jpg
foo-1.jpg
foo-2.jpg
foo-10.jpg
STA.jpg
STA-3.jpg
STA-20.jpg
bar-2.jpg
bar-10.jpg
bar.jpg
foo-1.jpg
foo-2.jpg
foo-10.jpg
foo.jpg
STA-3.jpg
STA-20.jpg
STA.jpg

The Finder way would be fine, but not ideal, as it leaves foo.jpg below foo-1.jpg

Here are commands I tried:

for file in *; do echo $file; done for file in *(n); do echo $file; done
STA-20.jpg
STA-3.jpg
STA.jpg
bar-10.jpg
bar-2.jpg
bar.jpg
foo-1.jpg
foo-10.jpg
foo-2.jpg
foo.jpg
STA-3.jpg
STA-20.jpg
STA.jpg
bar-2.jpg
bar-10.jpg
bar.jpg
foo-1.jpg
foo-2.jpg
foo-10.jpg
foo.jpg

The latter brings in numerical sort order and is the closest I could get to Finder, but leaves uppercase STA* files above lowercase bar* ones.

I tried to use glob qualifier cause it looked like the simplest syntax, but if it's not achievable – please suggest other ways to execute commands on multiple files after sorting them the way I want.

2 Answers 2

2

This will generate a sorted list similar to Finder's:

lc() { REPLY=$REPLY:l }
print -rl -- *(no+lc)

Output:

bar-2.jpg
bar-10.jpg
bar.jpg
foo-1.jpg
foo-2.jpg
foo-10.jpg
foo.jpg
STA-3.jpg
STA-20.jpg
STA.jpg

This adds the o+ glob qualifier, which can execute shell code to supply a value for sorting that is different from the original file name. Here the code in the function lc changes the sorting filename to lowercase with the :l modifier, so that the sort is case-insensitive. Note that this does not change the displayed filename.

1
  • Thanks! Based on this I was able to reach what I defined as "My desired sort order": lc() { REPLY=${(L)REPLY%.*} }; print -rl -- *(no+lc) – this changes the sorting filename to [lowercase without extension]. Dec 13, 2023 at 4:37
3

You can use case-insensitive (-f) version sorting (-V) with sort:

cd <your directory>
while IFS= read -r -d $'\0' filename; do
  # <your command(s) accepting each filename here>
  printf "%q\n" "$filename"
done < <(find . -type f -print0 | sort -z -f -V)

Or more efficiently if your command(s) can slurp all filenames:

cd <your directory>
filenames=()
while IFS= read -r -d $'\0' filename; do
  filenames+=( "$filename" )
done < <(find . -type f -print0 | sort -z -f -V)
# <your command(s) accepting all filenames (more efficient if possible)>
printf "%q\n" "${filenames[@]}"

These are safe, portable ways to execute commands for a list of filenames. Both output:

./bar.jpg
./bar-2.jpg
./bar-10.jpg
./foo.jpg
./foo-1.jpg
./foo-2.jpg
./foo-10.jpg
./STA.jpg
./STA-3.jpg
./STA-20.jpg

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .