From all the little cool *nix shell things this one is by far my favourite. I’m always on the hunt for some new small improvement to my workflow, and hear me out - that one is super, super handy.
The setup
Brace expansion enables us to generate arbitrary strings. How can that be handy? I use it most often while renaming files. Let’s say that you’re in your home directory and you want to rename a file that’s a few directories deep, so you run this command:
mv ~/some/directory/deep/in/your/system/file.md ~/some/directory/deep/in/your/system/file.txt
Typing that out is not that hard and you can always use tab-expansion to jump through directories, but you’ll still have to do that. Lucikly for us, you won’t have to anymore. If you think about this for a while, you will notice that both paths (duh) only differ in the file extension. If only there was a way to do it better…
Enter brace expansions
Expressions in braces in a bash script are expanded to a Cartesian product - all combinations of a given set. For a set of two, there are only two combinations, therefore:
echo "hello_{world,there}"
Expands to:
hello_world hello_there
Something really magical happened here. We’ve typed out the *_hello*
part once, but we got two strings as an output - exactly as expected from a Cartesian product of a set of two items (the words ‘world’ and ‘there’).
So how does it solve our problem?
Like that:
mv ~/some/directory/deep/in/your/system/file.{md,txt}
The savings in typing are gigantic! This is what I use brace expansion 99.9% of the time for, but the possibilities are way greater.
Extra examples
If you’ve been paying attention to math classes in high school you may remember how cartesian product works. Although as I said - I basicaly don’t use this for anything else but renaming files, here are some extra things you could do with brace expansion:
Make 10 files at once:
The syntax for brace expansions can take numbers as well, so that the following expression will produce 10 different files:
touch important_file{0..10}.txt
After running ls you can see that we have created 10 files in the current directory:
important_0.txt
important_1.txt
important_10.txt
important_2.txt
important_3.txt
important_4.txt
important_5.txt
important_6.txt
important_7.txt
important_8.txt
important_9.txt
Make 3 folders, with 2 subfolders each, with 3 files each in it
mkdir -p {breakfast,lunch,dinner}/{main,dessert}/recipe_{1..3}.txt
.
├── breakfast
│ ├── dessert
│ │ ├── recipe_1.txt
│ │ ├── recipe_2.txt
│ │ └── recipe_3.txt
│ └── main
│ ├── recipe_1.txt
│ ├── recipe_2.txt
│ └── recipe_3.txt
├── dinner
│ ├── dessert
│ │ ├── recipe_1.txt
│ │ ├── recipe_2.txt
│ │ └── recipe_3.txt
│ └── main
│ ├── recipe_1.txt
│ ├── recipe_2.txt
│ └── recipe_3.txt
└── lunch
├── dessert
│ ├── recipe_1.txt
│ ├── recipe_2.txt
│ └── recipe_3.txt
└── main
├── recipe_1.txt
├── recipe_2.txt
└── recipe_3.txt
It’s starting to show it’s power!
Step parameter
If you don’t want to include all results of a Cartesian product in a given numerical set, you can specify the increment, which can be positive or negative - for when you want to count backwards.
$ echo {3..-12..2}
3 2 1 0 -1 -2
$ echo {3..-12..-2}
-11 -9 -7 -5 -3 -1 1 3
Not only numbers
It’s possible to use letters in those too:
$ echo {a..f}
a b c d e f
$ echo {z..a}
z y x w v u t s r q p o n m l k j i h g f e d c b a
$ echo {z..A}
z y x w v u t s r q p o n m l k j i h g f e d c b a ` _ ^ ] \ [ Z Y X W V U T S R Q P O N M L K J I H G F E D C B A
$ echo {z..5}
z y x w v u t s r q p o n m l k j i h g f e d c b a ` _ ^ ] \ [ Z Y X W V U T S R Q P O N M L K J I H G F E D C B A @ ? > = < ; : 9 8 7 6 5
Further reading
You can’t go wrong with reading the GNU bash manual. I found this guide to be particularly nice to read and here’s a little more advanced one if you’re feeling comfortable.