Creating multiple directories with mkdir and character range

157 Views Asked by At

My goal is to automate the creation of multiple directories that end with numerical indexes: dir03 dir04 ... dir09

I tried the following command: mkdir dir0[3-9].

As the result, the shell created a directory named dir0[3-9]

I did try mkdir dir0{3,4,5,6,7,8,9]. It works but it's less convenient.

What are the underlying reasons why mkdir dir0[3-9] doesn't work as intended?

1

There are 1 best solutions below

1
Ed Morton On BEST ANSWER

You got the syntax a bit wrong:

  • You used [3-9] as used in pattern matching for matching an existing range of values (a range expression in globbing, used in filename expansion and a few other contexts, and also a range expression in regular expressions).
  • You need {3..9} as used in brace expansion for generating a new range of values (a sequence expression):
$ echo mkdir dir0{3..9}
mkdir dir03 dir04 dir05 dir06 dir07 dir08 dir09

remove the echo when happy with the output.

So we use {3..9} to generate a new range of values:

$ mkdir dir0{3..9}

$ printf '%s\n' {3..9}
3
4
5
6
7
8
9

and [3-9] to match an existing range of values:

$ find . -name 'dir0[3-9]'
./dir03
./dir04
./dir05
./dir06
./dir07
./dir08
./dir09

$ printf '%s\n' {0..10} | grep '[3-9]'
3
4
5
6
7
8
9

Note that they look similar but the semantics are different between range expressions in brace expansion and pattern matching, for example in brace expansion {1..20} expands to the numbers 1 through 20 (because ranges start/end with a potentially multi-digit number or a character) while in a range expression [1-20] expands to just the 3 numbers 1, 2, and 0 (because ranges always start/end with a character, not a string nor a multi-digit number, and so it means the set of the digits 1-2 and the digit 0).

$ printf '%s\n' {1..20} | tr '\n' ' '
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

$ printf '%s\n' {1..20} | grep '[1-20]' | tr '\n' ' '
1 2 10 11 12 13 14 15 16 17 18 19 20

$ printf '%s\n' {9..20} | tr '\n' ' '
9 10 11 12 13 14 15 16 17 18 19 20

$ printf '%s\n' {9..20} | grep '[9-20]' | tr '\n' ' '
grep: Invalid range end

That last result is because the range in pattern matching always has to be increasing so 9-2 (the range before 0 in [9-20]) is invalid in a regexp:

$ printf '%s\n' {9..20} | grep '[2-9]' | tr '\n' ' '
9 12 13 14 15 16 17 18 19 20

$ printf '%s\n' {9..20} | grep '[9-2]' | tr '\n' ' '
grep: Invalid range end

and in globbing:

$ ls -d dir0[2-9]
dir03  dir04  dir05  dir06  dir07  dir08  dir09

$ ls -d dir0[9-2]
ls: cannot access 'dir0[9-2]': No such file or directory

but is valid in brace expansion:

$ printf '%s\n' {2..9} | tr '\n' ' '
2 3 4 5 6 7 8 9

$ printf '%s\n' {9..2} | tr '\n' ' '
9 8 7 6 5 4 3 2