Multidigit ranges of files in lexicographical order in zsh

15,113

Solution 1

[2-57] is a character set consisting of 2, 3, 4, 5 and 7, in zsh and every other wildcard and regexp syntax out there. Your glob pattern *[2-57] matches every filename whose last character is one of those five digits.

I think you are misremembering the syntax of the [m,n] glob qualifier. Glob qualifiers always go in parentheses at the end of the pattern, and the range separator is a comma. The pattern *([2,57]) expands to the 2nd, 3rd, …, 57th matches. The default expansion order is lexicographic (with some special magic to sort numbers in numeric order if the numeric_glob_sort option is set); you can control it with the o or O glob qualifier (e.g. *(om[2,57]) to match the 57 most recent file except the one most recent file).

for x in /foo/bar/*([2,57]); do print $x; done

Not what you asked for, but related and possibly useful to future readers: if you want to enumerate files 2 to 57 whether they exist or not, you can use a range brace expression. This feature also exists in bash and ksh.

echo hello{2..57}

And if you want to match files whose name contains a number between 2 and 57, you can use the pattern <2-57>. This is specific to zsh.

$ ls
file1 file2 file3 file57 file58
$ echo file<2-57>
file2 file3 file57

Note that a pattern like *<2-57> is likely not to do what you expect, because the * could match digits too. For example, file58 matches *<2-57>, with file5 matching the * part and 8 matching the <2-57> part. The pattern *[^0-9]<2-57> avoids this issue.

Solution 2

but zsh apparently thinks I am asking for the files 2 to 5 (or something like that) instead of files 2 to 57. Any thoughts why?

Because [] brackets indicate a list of matched characters (which can be digits), not numbers interpreted mathematically. Such pattern is matched against a single character. The list can contain ranges, but of digits or letters. [2-57] match expands to "All digits in the range from 2 to 5 and a 7".

To match numbers from 2 to 57, it would be easier to use a sequence expression instead of a globbing pattern (or together with such):

for x in /foo/bar/*{2..57}; do print $x; done

Edit: But this, unfortunately, will not give you lexicographical order of all listed files - they'd be grouped by common number endings due to shell expansion.

Share:
15,113

Related videos on Youtube

Amelio Vazquez-Reina
Author by

Amelio Vazquez-Reina

I'm passionate about people, technology and research. Some of my favorite quotes: "Far better an approximate answer to the right question than an exact answer to the wrong question" -- J. Tukey, 1962. "Your title makes you a manager, your people make you a leader" -- Donna Dubinsky, quoted in "Trillion Dollar Coach", 2019.

Updated on September 18, 2022

Comments

  • Amelio Vazquez-Reina
    Amelio Vazquez-Reina over 1 year

    I would like to specify a range of files (in lexicographical order) with two integers (e.g. 2 to 57) in zsh by globbing.

    For example: "pick the files 2 to 57 in lexicographical order under the path that matches some globbing pattern".

    I thought using square brackets would do it

     for x in /foo/bar/*[2-57]; do print $x; done
    

    but zsh apparently thinks I am asking for the files 2 to 5 (or something like that) instead of files 2 to 57. Any thoughts why? How can I accomplish this?

  • Amelio Vazquez-Reina
    Amelio Vazquez-Reina over 12 years
    Thanks @rozcietrzewiacz! I should have thought about that. My files had numbers in them, so for a moment I thought [] was printing things in order..
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' over 12 years
    Your explanation of [2-57] is correct, but I don't think {2..57} (that's what you meant, right?) is relevant to what intrpc wants to do, which is “pick the files 2 to 57 in lexicographical order”.
  • rozcietrzewiacz
    rozcietrzewiacz over 12 years
    Thanks for the edit (time to go to sleep, I guess). And you're right - I'd forgotten about the lexicographical order part.
  • Amelio Vazquez-Reina
    Amelio Vazquez-Reina over 12 years
    You are right. I was misremembering the glob qualifier. Thanks for the careful explanation.
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' over 12 years
    @intrpc foo{8..11}bar expands to foo8bar foo9bar foo10bar foo11bar. This bit of expansion is unrelated to file names. Similarly, foo{eight,nine,ten,eleven}bar expands to fooeightbar fooninebar footenbar fooelevenbar. This is brace expansion in ksh/bash/zsh.