简体   繁体   中英

Possible to combine glob sort w/ alternation in zsh?

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM