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////$''}}//$''//})