Manipulating or Iterating over a serie of files and directories is a fairly common task when writing shell scripts on Linux. No one wants to write long sequences of commands manually when dealing with a large number of objects with sequential suffixes. This is where bash and seq can help.
This post covers how to generate a sequence of letters or numbers in Bash using Brace Expansions and with the seq command line.
What is the seq command?
Historically, the seq
command has been there to address a gap in the legacy Bourne shell which didn’t have any ways to iterate over numbers and the for
statement could only loop over a list of words. The seq
command can be found on some Unix systems as well as most Linux distributions. The command is now part of the GNU coreutils package. The seq
command only work for sequences of numbers, it does not work for sequences of letters.
Nowadays, most shells like Bash, have builtin numeric iteration which make seq
less commonly used.
What is Brace Expansion in Bash?
Bash perform various substitutions on its commands before executing them, the Brace Expansion is one of those substitution. The sytax use the curly brackets {}
as a shorthand to make commands shorter.
As an example, mv myImage.{jpeg,jpg}
is the same as mv myImage.jpeg myImage.jpg
.
⚠️ Brace expansion is performed before any other expansions. There is no interpretation of the expansion or the expression between braces. This mean that a variable inside the braces will not be expanded to its value.
How to print a sequence of number in Bash?
There is generally two ways to generate a sequence of numbers. You can either use the command line tool seq
or in Bash by using Brace Expansion {}
. The later is the preferred way to do as seq
is not guaranteed to be present on the system the script run. You can use a step
number to generate sequence of even and odd numbers or any other interval, example: {<start>..<end>[..<step>]}
# Using seq
[me@linux ~]$ seq 1 10
1 2 3 4 5 6 7 8 9 10
# Using seq with a step number (aka increment)
[me@linux ~]$ echo "Even Numbers:" $(seq 0 2 10)
Even Numbers: 0 2 4 6 8 10
# Using Brace Expansion
[me@linux ~]$ echo {1..10}
1 2 3 4 5 6 7 8 9 10
# Same as above with a Zero 0 as prefix
[me@linux ~]$ echo {01..10}
01 02 03 04 05 06 07 08 09 10
# Using Brace Expansion with a step number (aka increment)
[me@linux ~]$ echo "Even Numbers:" {0..10..2}
Even Numbers: 0 2 4 6 8 10
[me@linux ~]$ echo "Odd Numbers:" {1..10..2}
Odd Numbers: 1 3 5 7 9
How to print a sequence of letters in Bash?
# Sequence of letters with Brace Expansion
[me@linux ~]$ echo {a..g}
a b c d e f g
# Same as above with a step number
[me@linux ~]$ echo {a..g..2}
a c e g
How to use a variable while using the Bash Brace Expansion?
While the seq
command will support a variable as its parameters, the bash brace expansion will not. To workaround this, you will need to resort to a
bash for loop.
# CORRECT use of brace expansion
[me@linux ~]$ echo {1..5}
1 2 3 4 5
# INCORRECT
[me@linux ~]$ x=5; echo {1..$x}
{1..5}
# CORRECT alternative when using variables
[me@linux ~]$ x=5; for ((i=1; i<=x; i++)); do echo $i; done
1
2
3
4
5
Detailed Example with mkdir and Brace Expansion
Of course, just printing sequences is not very helpful, below is an example on applying the same sequence expansion when using mkdir
to build a deep tree of directories.
[me@linux ~]$ mkdir -p test/{1..10}/{1..10}
[me@linux ~]$ ls test/**/
test/1/:
1 10 2 3 4 5 6 7 8 9
test/10/:
1 10 2 3 4 5 6 7 8 9
test/2/:
1 10 2 3 4 5 6 7 8 9
test/3/:
1 10 2 3 4 5 6 7 8 9
test/4/:
1 10 2 3 4 5 6 7 8 9
test/5/:
1 10 2 3 4 5 6 7 8 9
test/6/:
1 10 2 3 4 5 6 7 8 9
test/7/:
1 10 2 3 4 5 6 7 8 9
test/8/:
1 10 2 3 4 5 6 7 8 9
test/9/:
1 10 2 3 4 5 6 7 8 9
👉 See more advanced examples of Braces Expansion in my post on How To Use Bash Wildcards for Globbing