Looking for a shell-only[1] way to take this list of dirs:
foo1.1
foo1.10
foo1.10/bar
foo1.2
foo1.3
foo1.3/bar
foo1.4
foo1.5
foo1.5/bar
foo1.6
foo1.7
foo1.8
foo1.9
foo2.1
And return it sorted numerically, with the subdirs showing up right after their parent:
foo1.1
foo1.2
foo1.3
foo1.3/bar
foo1.4
foo1.5
foo1.5/bar
foo1.6
foo1.7
foo1.8
foo1.9
foo1.10
foo1.10/bar
foo2.1
(*|*/bar)(n)
is rejected as a bad pattern, while */{,bar}(n)
expands to */(n) ~/bar(n)
so the subdirs show up at the end.
[1] I need this to be able to work on a wide variety of systems, so using GNU sort’s -V or GNU ls’s -v or the like won’t work.
Advertisement
Answer
You can easily do it in two steps: list, then sort.
dirs=(* */bar)
dirs=(${(on)dirs})
This relies on /
being sorted before any suffix of an existing directory, so it won’t work if you have directories with names like foo1.3-qux
(it would be sorted between foo1.3
and foo1.3/bar
. Since all the files are directories, you could work around that with a temporary /
suffix, if it’s ok to have foo1.3-qux
sorted before foo1.3
:
dirs=(*/ */bar)
dirs=(${${(on)dirs}%/})
For more robustness you can temporarily replace the /
characters by a null byte, which is both guaranteed to be sorted before any other (at least in the C locale) and not to appear in a file name.
dirs=(* */bar)
dirs=(${${(on)${dirs////$''}}//$''//})