how to use brace expansion characters in linux command line and shell scripts

If you are a Linux power user, you are probably working a lot with command shell both the command line interface (CLI) as well as shell scripts. Linux command line provides a lot of flexibility and powerful options when working with different commands: regular expressions, wildcards, meta characters, operators, file and i/o redirection, brace expansion etc etc.

We will see how you can use brace expansion to perform various tasks while matching against various file and directory names or any command arguments. Understanding brace expansion, which uses curly braces ({}) will make many of the multi file commands easier to perform. Brace expansions are normally used to match against multiple file names easily when performing any tasks that takes file names as arguments.

The brace notation in general follows the following pattern…

  • {string1,string2,string3}: a comma separated list of strings or characters
  • {start..end}: A start and end value, either numerical or string that denotes the range
  • {start..end..incr}: Same as above, but with an increment value

It is probably easier to explain using different examples. Let’s say we want create three different files with the file names test1, test2 and test3. One way to do this is to use the touch command and specify the file names, as shown below

$ touch test1 test2 test3

This works fine but we had to type out each of the file names individually which can be cumbersome. We can use brace expansion to achieve the same thing….

$ touch test{1,2,3}

The above command will create 3 files with the names test1, test2, and test3. Each of the characters (or strings) inside the curly braces ({…}) is expanded and appended to the argument or the string that precedes it. We can use this feature to match against more complicated examples.

The characters inside the curly braces does not have to numbers but can be any character or a set of characters or a combination of numbers and strings. However, each value inside the braces should be separated by a comma. So, you could have something like this:

$ touch test{one,2,three,4,5}

You can use any number of these braces with in the same argument to create various different combinations or expansions. So, we could match file names as shown below

$ touch ${tom,dick,harry}-food-{breakfast,lunch,dinner}

The above example will create 9 different files such as : tom-food-breakfast, dick-food-lunch, harry-food-lunch etc etc. I am sure you can work out the rest of it.

Now, again let’s say we want to create hundreds of different files in the following combination: a1, a2…a10,b1,b2…..z10. We could list out all 26 characters and 10 integers separated by comma but that is again way too much of keystrokes. We can use dots (..) to enumerate consecutive characters within the curly braces or specify ranges. So, the above command becomes

$ touch {a..z}{1..10}

You can use this notation with any command that accept command line arguments. Let’s we now want to delete all files that start with e or f and end with 3, 4 or 5. We can do this with the rm command as follows

$ rm -fv {e,f}{3..5}

Another feature of this notation is that the brace notation itself can be nested. That means we can create files a1, a2,..a9, b10, b11…b20, c21,..c30 etc by using a expansion notation as shown in the example below.

$ touch {a{1..9},b{10..20},c{21..30}}

You can already see how this can be used in various different scenarios. To create a folder for every month for several years, you can use the example below. The zero padded range expansion is new in bash 4.0.

$ mkdir {2015..2017}-{01..12}

To delete images that have the extensions of either jpg, png or jpeg in two different folders temp or tmp.

$ rm -fv /home/tom/{temp,tmp}/image.{jpg,png,jpeg}

If you writing complicated nested expansion, then it is always a good idea to print it out and verify it first. You don’t want to accidentally delete the wrong file just because you missed a comma or a brace. You can do this by using echo command.

$ echo /home/tom/{temp,tmp{1..4}}/image.{jpg,png,jpeg}

You can start the comma separated list inside the brace with a comma which equates to an empty character or field. This can be quite useful when you want the enumeration to include a empty combination. For example, the following will match to the file name as well the backup files: test.txt, test.txt.backup, test.txt.bkp.

$ ls -la /home/tom/test.txt{,.backup,.bkp}

In the latest bash shell (bash ver. 4.0 and above), you can specify an increment along with the ranges in the brace expansion. This works for both numerical and string ranges. So, you can use the following example to create file names that contain odd numbers and characters that are interleaved by 3.

$ touch test-{a..z..3}-{1..100..2}

Another of the uses of brace expansion notation is the ability to be used within the for loop, especially to generate sequences that are not numerical in nature. For example, you could do something like this:

$ for i in {a..z}; do echo $i; done

The examples we have used are mostly on command line, but you can use all of these brace expansion with in the shell scripts as well, just as the examples shown above.