Apr 18,
2019

Why I use Zsh

Because "glob qualifiers" expansion with numeric sort order it's boss, that's why!

Greetings Netizens.

Today I was confronted with a silly problem, i.e. how to apply a series of patches in order where their name was ending with a numeric index.

For about ten files it's not a task that should deserve a blog post, the default glob expansion is sorted as anyone would expect:

$ for i in {0..9}; do touch foo-${i}; done
$ echo foo-*
foo-0 foo-1 foo-2 foo-3 foo-4 foo-5 foo-6 foo-7 foo-8 foo-9
$

But what would happens when dealing with a dozen or more files?

Well, for the benefit of the unaware reader, the default expansion uses the alphabetical sorting, or better the sorting is based on the lexicographical order of the ASCII/UTF1 characters encoding, as showed on the example below2:

$ for i in {0..15}; do touch foo-${i}; done
$ echo foo-*
foo-0 foo-1 foo-10 foo-11 foo-12 foo-13 foo-14 foo-15 foo-2 foo-3 foo-4 foo-5 foo-6 foo-7 foo-8 foo-9
$

Not the order that I would like the patches applied.

Bummer, right?

ʕ ಡ ﹏ ಡ ʔ

Well, one possible solution it's suggested in my own example, using a loop with a numeric index, also I'm adding a file existence check before the action:

$ for i in {0..15}; do [[ -f foo-${i} ]] && echo foo-${i}; done
foo-0
foo-1

[...]

foo-9
foo-10
foo-11
foo-12
foo-13
foo-14
foo-15
$

Feasible, even if a little verbose.

I'm sure someone of the Zsh team was bummed too because there's an option called NUMERIC_GLOB_SORT, not set by default tho, that literally states if numeric filenames are matched by a filename generation pattern, the filenames are sorted numerically rather than lexicographically. This means that one has to remember to check if the option is enabled or to apply it with setopt if not.

Can we do better? Sure, enter the glob qualifiers.

٩( ′ㅂ`)و

Briefly, with its qualifiers, Zsh can basically filter the result of a glob expansion, for example *bar*(/) returns a list of all the directories that contains the "bar" string in the current directory3. There is a huge list of qualifiers described on the official documentation and nearly at the end of the page there's the n qualifier which description states it sets the NUMERIC_GLOB_SORT option for the current pattern:

$ for i in {0..15}; do touch foo-${i}; done
$ echo foo-*
foo-0 foo-1 foo-10 foo-11 foo-12 foo-13 foo-14 foo-15 foo-2 foo-3 foo-4 foo-5 foo-6 foo-7 foo-8 foo-9
$ echo foo-*(n)
foo-0 foo-1 foo-2 foo-3 foo-4 foo-5 foo-6 foo-7 foo-8 foo-9 foo-10 foo-11 foo-12 foo-13 foo-14 foo-15
$

Neat, isn't?

Keybind of the Day

Not strictly related to the above, but I've recently found out that on Zsh there's a function called _most_recent_file bound as default to "Ctrl-x m". I think I've always had a couple of aliases on my .zshrc to list the last modified file or directory using — guess what — glob qualifiers4, but this is way better as it can select the most recent file and also expand a glob. For example cat *.py followed by "Ctrl-x m" would insert in the edit buffer the name of the last Python file modified in the current directory, even if more recent files are present.


  1. sorting UTF multibyte encoded characters has it's own can of worms ↩︎

  2. instead of Bash, Zsh can also expands in-line pressing two times the Tab key or Ctrl-i ↩︎

  3. also Zsh will recursively expand/search files from the current directory when using two glob symbols, like **bar*(/) ↩︎

  4. for the curious reader *(.om[1]) and *(/om[1]) respectively for files and directories, so the first element of the list sorted by modification date ↩︎

zsh