Quick Links

Brace expansion is a useful technique to generate lists of strings that can be used in scripts and aliases and on the Linux command line. Save time and avoid mistakes by typing less.

Brace Expansion

Before the Bash shell executes a command in a terminal window or a line in a script, it checks whether it needs to perform any substitutions on the command. Variable names are replaced by their values, aliases are replaced by the commands they're shorthand for, and any expansion is performed. Brace expansion is one form of expansion supported by Bash.

Brace expansion is available in modern shells, but it might be missing from some old shells. If you're going to use brace expansion in scripts, make sure that you invoke a shell that supports brace expansion, such as Bash:

        #!/bin/bash
    

We'll be using Bash for our examples.

Generating lists of strings might seem more of a novelty than a benefit, but it does offer some functionality that can save time and keystrokes. Often, it can provide a simple and elegant solution to a problem or requirement.

Simple Expansions

A brace expansion is contained between a pair of braces "{}". It can be a list of comma-separated items or a range specifier. Spaces are not permitted inside the braces unless you've wrapped the string in quotation marks "

        "
    

."

For a comma-separated list, the expansion process takes each element in turn and passes it to the calling command. In this example, it's echo that simply prints them in the terminal window. Note that the commas are ignored.

echo {one,two,three,four}

A list can be words or digits.

echo {1,2,3,4}

The order of the list elements is completely arbitrary.

echo (4,2,3,1)

echo {one,two,three,four} in a terminal window

An expansion range has a start and an end character connected with two periods " .. " without any whitespace. All of the missing list elements are provided automatically by the expansion so that the entire range from start character to end character is created.

This will print the digits from 1 to 10.

echo {1..10}

echo {1..10} in a terminal window

The numbering is arbitrary. It does not have to start at one.

echo {3..12}

echo {3..12} in a terminal window in a terminal window in a terminal window in a terminal window in a terminal window in a terminal window in a terminal window in a terminal window

Ranges can be specified so that they run backward. This will generate a list from five down to one.

echo {5..1}

echo {5..1} in a terminal window

Ranges can include negative numbers.

echo {4..-4}

echo {4..-4} in a terminal window

As we previously pointed out, a range has a start and an end character. It doesn't have to be a number. It can be a letter.

echo {q..v}

The letters can run backward, too.

echo {f..a}

echo {q..v} in a terminal window

Using Brace Expansion with Loops

You can use brace expansion with ranges in loops in scripts.

for i in {3..7}
    

do

echo $i

done

Brace expansion ranges let you use characters as the loop variable.

for i in {m..q}
    

do

echo $i

done

Loops are usually used in scripts, but there's nothing to stop you from typing them into the command line to see what will happen.

for i in {3..7}; do echo $i; done

for i in {3..7}; do echo $i; done in a terminal window

for i in {m..q}; do echo $i; done

for i in {m..q}; do echo $i; done in a terminal window

Concatenating and Nesting

Two adjacent expansions don't act independently one after the other. They interoperate. Each element in the first expansion is acted on by each element in the second expansion.

echo {q..v}{1..3}

echo {q..v}{1..3} in a terminal window

Expansions can also be nested. A nested expansion will act on the element immediately preceding it.

echo {part-1,part-2{a,b,c,d},part-3}

echo {part-1,part-2{a..d},part-3} in a terminal window in a terminal window in a terminal window in a terminal window in a terminal window in a terminal window

You can also nest expansions by creating a comma-delimited list of range expansions.

echo {{5..0},{1..5}}

echo {{5..0},{1..5}} in a terminal window

Preamble and Postscript

You can place text before and after a brace expansion to have that text included in the results of the expansion. Text put in front of an expansion is called the preamble, while text placed behind a brace expansion is called the postscript.

This command uses a preamble.

echo chapter{1..3}

echo chapter{1..3} in a terminal window

This example uses a postscript:

echo {contents,paper,bilbiography}.md

echo {contents,paper,bilbiography}.md in a terminal window

And this command uses both.

echo chapter-{1..4}.md

echo chapter-{1..4}.md in a terminal window

Expanding File Names and Directories

As you've probably guessed by now, one of the main uses of brace expansions is to create file and directory names that can be passed to other commands. We've been using echo as a convenient way to see exactly what happens when an expansion is triggered. You can substitute any command that takes filenames or directory names as input and use brace expansion with it.

To quickly create some files, use touch:

touch file-{1..4}.txt

ls *.txt

touch file-{1..4}.txt in a terminal window

If you have many files with the same base name but different file extensions and you want to perform an operation on a subset of them, brace expansions can help. Here, we're compressing a subset of files that have "program" as the basename into a ZIP file called "source-code.zip."

Development directories contain lots of files that will have the same basename as your main program. Usually, you don't want to back up or distribute files like ".o" object files. This is a neat way to only include the file types of interest.

zip source-code program{.c,.h,.css}

zip source-code program{.c,.h,.css} in a terminal window

This command will make a copy of a file and append ".bak" to it, making a backup copy of the original file. An interesting point to note is that the brace expansion contains a comma-separated list, but the first element is empty. If we hadn't included the comma, the expansion wouldn't have taken place.

cp brace/new/prog-1.c{,.bak}

ls brace/new/prog-1.c.bak

cp brace/new/prog-1.c{,.bak} in a terminal window

To perform some action on two files in different directories, we can use a brace expansion in the path to the files.

In this example, the "brace" directory contains two subdirectories, one called "new" and one called "old." They contain different versions of the same set of source code files. We'll use the diff program to see the differences between the two versions of "prog-1.c."

diff brace/{new,old}/prog-1.c

diff brace/{new,old}/prog-1.c in a terminal window

If you have a standard skeleton of directories that you need to create at the start of a project, you can create them quickly using brace expansion. The  mkdir -p (parent) option creates any missing parent directories when a child directory is created.

mkdir -p {source,build,man,help{/pages,/yelp,/images}}

tree

mkdir -p {source,build,man,help{/pages,/yelp,/images}} in a terminal window

You can use brace expansion with wget to download multiple files.

In this command, we're going to download files from two directories, called "test1" and "test2." Each directory holds two files called "picture1" and "picture2."

wget https://www.URL-of-your-choice.com/test{1,2}/picture{001,002}.jpg

wget https://www.URL-of-your-choice.com/test{1,2}/picture{001,002}.jpg in a terminal window

Listing the files shows you the files that were retrieved and how wget renames files to avoid name clashes with existing files.

ls picture*.*

ls picture*.* in a terminal window

Embrace the Brace

Related: 15 Special Characters You Need to Know for BashIt seems that brace expansion is another one of Linux's best-kept secrets. Many people tell me that they've never heard of brace expansion, while others inform me that it's one of their favorite command-line tricks.

Give it a try and it just might find its way into your set of command-line go-to tricks.