Brace Expansion: {1..10} and More
What You'll Learn
- What brace expansion actually does
- How to write
{1..10}(sequences) and{a,b,c}(lists) - How to combine it with
mkdirandcpto shrink repetitive work into one line - The key difference between brace expansion and
*(wildcards)
Quick Summary
- Need a sequence →
{1..10} - Need a fixed set of words →
{a,b,c} - Make a backup →
cp file.txt{,.bak}
1. What Is Brace Expansion?
Conclusion: Brace expansion is a bash feature that expands
{ }into multiple strings at once.
file1.txt through file5.txt. Do I really have to type touch file1.txt five times?touch file{1..5}.txt{1..5}?{ } (the braces), turning it into five strings: file1.txt file2.txt file3.txt file4.txt file5.txt.Let's see what's really happening with echo. Since echo simply prints the strings it receives, it's perfect for inspecting the result of an expansion.
$ echo file{1..5}.txtfile1.txt file2.txt file3.txt file4.txt file5.txt
What brace expansion really is
The shell expands the contents of { } into multiple strings before running the command.
So touch file{1..5}.txt is the same as running touch file1.txt file2.txt file3.txt file4.txt file5.txt.
2. Making Sequences: {start..end}
Conclusion:
{1..10}generates the sequence 1 to 10. Letters, descending, and reversed ranges use the same syntax.
2-1. Numeric sequences
$ echo {1..10}1 2 3 4 5 6 7 8 9 10
.. (two dots) means "from here to here."{start..end} makes a sequence. The key is it's two dots, not a comma.2-2. Letter sequences
$ echo {a..e}a b c d e
2-3. Descending order works too
Make the end smaller than the start, and the order reverses.
$ echo {5..1}5 4 3 2 1
3. Zero-Padding and Stepping
Conclusion:
{01..10}zero-pads, and{1..10..2}sets the increment (step).
3-1. Zero-padding (aligning digit width)
When you want filenames to line up, start the number with a leading 0 to zero-pad.
$ echo {01..10}01 02 03 04 05 06 07 08 09 10
Zero-padding keeps file ordering stable. file1 file2 ... file10 sorts as file1, file10, file2... by name, but file01 file02 ... file10 sorts the way you expect.
3-2. Specifying a step
{start..end..step} controls how much to skip each time.
$ echo {0..20..5}0 5 10 15 20
$ echo {1..10..2}1 3 5 7 9
4. Making Lists: {a,b,c}
Conclusion:
{a,b,c}lays out comma-separated words as-is. Use it when the items aren't a sequence.
When you want to lay out a fixed set of words rather than a sequence, use commas.
$ echo {apple,banana,cherry}apple banana cherry
Don't put spaces around the commas
Writing {a, b, c} with spaces breaks brace expansion, and it's treated as plain text. Write {a,b,c} with no spaces.
.. is for sequences and , is for lists. I've got the distinction!{1..100} out as {1,2,3,...,100} would be painful, so use a sequence for that, and a list for a handful of fixed words.5. Prefixes, Suffixes, and Combining
Conclusion: Text before or after the braces is distributed to each item. Multiple braces side by side generate every combination.
5-1. Prefix and suffix
Text before or after the braces is distributed to every item.
$ echo image_{1..3}.pngimage_1.png image_2.png image_3.png
5-2. Multiple braces (cartesian product)
Put two or more braces side by side and you get every combination.
$ echo {A,B}{1,2}A1 A2 B1 B2
{A,B,C}{1,2,3,4}).6. Techniques That Pay Off at Work
Conclusion: Bulk directory creation, backups, and renames are classic patterns you can shrink to one line with brace expansion.
6-1. Create directories all at once
$ mkdir -p project/{src,test,docs}This makes src, test, and docs under project in one shot. -p means "create parent directories if they don't exist."
6-2. Make a backup instantly
One side of the brace can be empty. {,.bak} generates "add nothing" and "add .bak."
$ cp config.yml{,.bak}What cp config.yml{,.bak} does
It's the same as this command:
$ cp config.yml config.yml.bak
{,.bak} expands to config.yml and config.yml.bak, forming cp source dest.
6-3. Renaming works too
$ mv report.txt{,_old}This renames report.txt to report.txt_old. No need to type the same filename twice.
7. Brace Expansion vs. * (Wildcards)
Conclusion: Brace expansion "generates" strings.
*"matches" existing files. Generating something that doesn't exist yet is what makes brace expansion special.
* also handles multiple files at once. What's the difference?* looks for files that exist right now and matches them. But brace expansion creates strings even if no file exists. That's why you can write names for files you're about to create.Generate (brace) or match (glob)?
| Syntax | Behavior | When no file exists |
|---|---|---|
{1..3}.txt |
Generates strings | strings are still created |
*.txt |
Matches existing files | matches nothing |
touch file{1..3}.txt (about to create) is fine. touch file*.txt (don't exist yet) won't do what you want.
8. Common Pitfalls
Conclusion: Stray spaces, using variables directly, and the dot count are the classic mistakes. To use a
$variablein a range, reach forseqoreval.
8-1. Putting a space inside
$ echo { 1..3 }{ 1..3 }
A space stops it from being recognized as brace expansion, and it prints as-is. Write {1..3} with no spaces.
8-2. You can't use a variable directly in a range
$ n=5
$ echo {1..$n}{1..5}
Brace expansion runs before variable expansion, so $n in {1..$n} isn't a number yet. To loop over a range with a variable, use seq.
$ echo $(seq 1 $n)
1 2 3 4 5
8-3. A single dot isn't a sequence
{1.5} with a single dot is not a sequence. A sequence always uses two dots ({1..5}).
9. Mini Exercises: Try It Yourself
Conclusion: Three tasks (numbered files, bulk directories, backup) let you verify the basics of brace expansion by hand.
echo to check the result first.Task 1: Create seven log files at once, from day01.log to day07.log.
Show hint
Combine the zero-padded sequence {01..07} with touch.
Sample answer
$ touch day{01..07}.logTask 2: Create bin, lib, conf, and log under a myapp directory all at once.
Show hint
Use the list {bin,lib,conf,log} with mkdir -p.
Sample answer
$ mkdir -p myapp/{bin,lib,conf,log}Task 3: Make a backup notes.md.bak of notes.md with a single command.
Show hint
Use a brace with one empty side: {,.bak}.
Sample answer
$ cp notes.md{,.bak}10. Copy-Paste Templates
Conclusion: Keep the common patterns for sequences, lists, combinations, and backups at hand.
Handy patterns to keep around
# Create numbered files
touch file{1..10}.txt
# Zero-padded sequence
touch log{01..12}.txt
# Step by 10
echo {0..100..10}
# A fixed list of words
mkdir -p project/{src,test,docs}
# Every combination (cartesian product)
echo {2025,2026}-{01..12}
# Make a backup
cp config.yml{,.bak}
# Safety check: prefix with echo to preview before running
echo touch file{1..10}.txt