≡ Menu

Bash Brace Expansion Tutorial: 6 Examples of Expanding Expressions within Braces

Bash Shell expansion

One of the operation of the shell when it analyzes the input is Shell expansion. Bash provides different types of expansion. In this article let us review an important expansion — “Brace expansion”.

This article is part of our on-going Bash Tutorial series.

Brace Expansion

Brace expansion is used to generate arbitrary strings. Brace expansion allows you to create multiple modified command line arguments out of a single argument. The specified strings are used to generate all possible combination with the optional surrounding preambles and postscripts. The preamble is prefixed to each string contained within the braces, and the postscript is then appended to each resulting string, expanding left to right.

$ echo last{mce,boot,xorg}.log
lastmce.log lastboot.log lastxorg.log

where last is Preamble and .log is the postscript

The above echo statement avoids you to specifying the three log files separately. If you want to view the content of the last boot log, mce log and xorg log you can use the brace expansion as shown in the above echo statement.

1. Example for Backup using brace expansion

$ cat bkup.sh
set -x # expand the commands
da=`date +%F`
cp $da.log{,.bak}

$ ./bkup.sh
++ date +%F
+ da=2010-05-28
+ cp 2010-05-28.log 2010-05-28.log.bak

In the above backup script, it copies the current date log file with the extension .bak. The first element is empty in the braces, so first element will have only preamble.

2. Example for Restore using brace expansion

$ cat restore.sh
set -x # expand the commands
da=`date +%F`
cp $da.log{.bak,}

$ ./restore.sh
++ date +%F
+ da=2010-05-28
+ cp 2010-05-28.log.bak 2010-05-28.log

In the restore script, the first element in the parameter is .bak where as second element is empty.

Also, refer to our earlier article on bash shell functions for additional reading.

3. Example for Brace Expansion without preamble and postscript

If there is no preamble and postscript, it just expands the elements given in the braces.

$ cat expand.sh
echo {oct,hex,dec,bin}

$ ./expand.sh
oct hex dec bin

Without the optional preamble and postscript strings, the result is just a space separated list of the given strings

Brace expansion for Ranges

Brace expansion expands the sequences also. The sequences can be of integers or characters.

4. Example for Integer and character sequences

$ cat sequence.sh
cat /var/log/messages.{1..3}
echo {a..f}{1..9}.txt

$ ./sequence.sh
May  9 01:18:29 x3 ntpd[2413]: time reset -0.132703 s
May  9 01:22:38 x3 ntpd[2413]: synchronized to LOCAL(0), stratum 10
May  9 01:23:44 x3 ntpd[2413]: synchronized to
May  9 01:47:48 x3 dhclient: DHCPREQUEST on eth0
May  9 01:47:48 x3 dhclient: DHCPACK from 23.42.38.201
..
..
a1.txt a2.txt a3.txt a4.txt b1.txt b2.txt b3.txt b4.txt c1.txt c2.txt c3.txt c4.txt

The first cat command, expands messages.1,messages.2 and messages.3 and displays the content. and in the next echo statement character and integer sequences are combined and used.

Sequences with increment value

In kshell brace expansion, you can use increment value, to generate the sequences.

Syntax:
<start>..<end>..<incr>

incr is numeric. You can use a negative integer, but the correct sign is deduced from the order of start and end.

5. Example for using Increment in sequences

$ ksh
$ echo /var/log/messages.{1..7..2}
/var/log/messages.1 /var/log/messages.3 /var/log/messages.5 /var/log/messages.7
$

Using this you could see the alternate days logfiles.

Pitfall in Brace expansion

Brace expansion does not expand bash variables, because the brace expansion is the very first step of the shell expansion, variable will be expanded later.

6. Example for Variables in expansion

If you see the output of the following two for statement, you could identify the above pitfall.

$ cat var_seq.sh
# Print 1 to 4 using sequences.
for i in {1..4}
do
        echo $i
done
start=1
end=4

# Print 1 to 4 using through variables
echo "Sequences expressed using variables"
for i in {$start..$end}
do
        echo $i
done

$ ./var_seq.sh
1
2
3
4
Sequences expressed using variables
{1..4}
Add your comment

If you enjoyed this article, you might also like..

  1. 50 Linux Sysadmin Tutorials
  2. 50 Most Frequently Used Linux Commands (With Examples)
  3. Top 25 Best Linux Performance Monitoring and Debugging Tools
  4. Mommy, I found it! – 15 Practical Linux Find Command Examples
  5. Linux 101 Hacks 2nd Edition eBook Linux 101 Hacks Book

Bash 101 Hacks Book Sed and Awk 101 Hacks Book Nagios Core 3 Book Vim 101 Hacks Book

Comments on this entry are closed.

  • Guido June 8, 2010, 4:54 am

    About 6. Example for Variables in expansion
    The solution to make the 2nd part working is to use “eval”:
    $ tail -n 6 var_seq.sh
    # Print 1 to 4 using through variables with eval
    echo “Sequences expressed using variables and eval”
    eval for i in {$start..$end}\
    \;do\
    echo \$i\
    \;done

  • Peter Stuge June 13, 2010, 10:16 pm

    Or run seq, though that incurs the cost of a fork:

    for i in $(seq $start $end); do echo $i; done

    when starting from 1, $start can be left out:

    for i in $(seq $end); do echo $i; done

    and seq can also be used with an increment, but it goes on the end:

    for i in $(seq $start $incr $end); do echo $i; done

  • Anonymous July 25, 2010, 7:37 pm

    With respect to using “seq” ; that isn’t portable. It exists on Linux only.

    BSD’s use “jot” Solaris has no equivalent; but I am not a full time Solaris Admin. Keeping scripts portable is a good thing; why write and rewrite and maintain the same script for various OS. IMHO it’s better to keep things portable.

    Shells vary between OS’s as well as user land tools. Well, you can use a “c style” loop to do counting and some tools exist across OS; “nl” and “sed” and “awk”… They can all be used to get a count. However, I would stick with the c-style loop. Sometime command line arguments change on tools. Just my .02 cents worth…

  • Joel July 7, 2015, 3:45 am

    Example 4 output is false :
    echo {a..f}{1..9}.txt
    should list a1.txt, a2.txt … a9.txt b1.txt … b9.txt ..but output stops at a4.txt, and letters stop at c instead of “f”

    either fix the output, or change the example to match the ouput (should then be :
    echo {a..c}{1..4}.txt