Apply brace expansion in "reverse order"

You could do:

$ eval echo '{a..c}'{1..3}
a1 b1 c1 a2 b2 c2 a3 b3 c3

Which then tells the shell to evaluate:

echo {a..c}1 {a..c}2 {a..c}3

For this particular case, I think that the option given by Stéphane Chazelas is the best one.

On the other hand, when you expand more complex things, this option doesn't scale well. So, you can achieve the same with this:

$ printf '%s\0' {a..c}{1..3} | sort -zk 1.2,1.2 | tr '\0' ' '

which returns:

a1 b1 c1 a2 b2 c2 a3 b3 c3

Seems a little messy, but now, I have a huge control in the order, just changing two chars in the command above; for example:

$ echo {a..b}{1..2}{a..b}{1..2}

this will expand to:

a1a1 a1a2 a1b1 a1b2 a2a1 a2a2 a2b1 a2b2 b1a1 b1a2 b1b1 b1b2 b2a1 b2a2 b2b1 b2b2

Suppose I want all the 1 in the second expansion, then the 2:

$ printf '%s\0' {a..b}{1..2}{a..b}{1..2} | sort -zk 1.2,1.2 | tr '\0' ' '
a1a1 a1a2 a1b1 a1b2 b1a1 b1a2 b1b1 b1b2 a2a1 a2a2 a2b1 a2b2 b2a1 b2a2 b2b1 b2b2

Suppose I want all the a in the third expansion, then the b:

$ printf '%s\0' {a..b}{1..2}{a..b}{1..2} | sort -zk 1.3,1.3 | tr '\0' ' '
a1a1 a1a2 a2a1 a2a2 b1a1 b1a2 b2a1 b2a2 a1b1 a1b2 a2b1 a2b2 b1b1 b1b2 b2b1 b2b2

Suppose I want all the 1 in the fourth expansion, then the 2:

$ printf '%s\0' {a..b}{1..2}{a..b}{1..2} | sort -zk 1.4,1.4 | tr '\0' ' '
a1a1 a1b1 a2a1 a2b1 b1a1 b1b1 b2a1 b2b1 a1a2 a1b2 a2a2 a2b2 b1a2 b1b2 b2a2 b2b2

Suppose I want all the 1a in the middle, then 1b, then 2a, then 2b:

$ printf '%s\0' {a..b}{1..2}{a..b}{1..2} | sort -zk 1.2,1.3 | tr '\0' ' '
a1a1 a1a2 b1a1 b1a2 a1b1 a1b2 b1b1 b1b2 a2a1 a2a2 b2a1 b2a2 a2b1 a2b2 b2b1 b2b2

You can even, just as easily, reverse any order in the expansions above, just adding an r to the previous command; for example, the last one:

$ printf '%s\0' {a..b}{1..2}{a..b}{1..2} | sort -rzk 1.2,1.3 | tr '\0' ' '
b2b2 b2b1 a2b2 a2b1 b2a2 b2a1 a2a2 a2a1 b1b2 b1b1 a1b2 a1b1 b1a2 b1a1 a1a2 a1a1

Note_1: usually, if this final expansion is going to be used as a list of arguments, the trailing space is not a problem; but if you want to get rid of it, you can add, to any of the commands above, for example | sed 's/ $//'; or even | sed 's/ $/\n/', to change that trailing space for a newline

Note_2: In the examples above, I've use subsets of two elements (i.e.: {a,b} and {1,2}) just for simplicity in the proof of concept: you can use subsets of any finite length, and the corresponding command, would be comparable.


bash, ksh, zsh

A one liner that works in (bash, ksh, zsh) (not all shells can do "Brace expansion" in reverse order):

$ echo {3..1}{c..a} | rev
a1 b1 c1 a2 b2 c2 a3 b3 c3

An alternative that use eval (which is still for bash, ksh, zsh and may be more cryptic) is:

$ eval echo '{a..c}'{1..3}
a1 b1 c1 a2 b2 c2 a3 b3 c3

To understand what happens, replace eval with echo:

$ echo echo '{a..c}'{1..3}
echo {a..c}1 {a..c}2 {a..c}3

The command executed (after eval expansion) is actually echo {a..c}1 {a..c}2 {a..c}3. Which expands as you want/need.

all shells

There are several shells without "brace expansions", so, not possible to use that for "all shells". We need a loop (with a trailing white space):

$ for i in 1 2 3; do for j in a b c; do printf "%s%s " "$j" "$i"; done; done; echo
a1 b1 c1 a2 b2 c2 a3 b3 c3 

If you must have no trailing space added:

s=""
for i in 1 2 3; do
    for j in a b c; do
        printf "%s%s%s" "$s" "$j" "$i"
        s=" "
    done
done
echo

Prints

a1 b1 c1 a2 b2 c2 a3 b3 c3

IF you need to do this for many values we need to use something similar to the brace expansion to generate a list of numbers $(seq 10). And, as seq can not generate a list of letters, we need to convert to ascii the numbers generated:

s=""
for i in $(seq 4); do
    for j in $(seq 5); do
        printf "%s\\$(printf %03o $((96+j)))%s" "$s" "$i"
        s=" "
    done
done
echo

prints:

a1 b1 c1 d1 e1 a2 b2 c2 d2 e2 a3 b3 c3 d3 e3 a4 b4 c4 d4 e4