The reason this does not work is because bash
performs brace expansion before command substitution (the Bash Reference Manual has some information about this). The substitution is not performed until after the brace expansion already occurred.
The only way you could do this is by using eval
, but do not do that in this case, as you will allow arbitrary command execution.
Instead you have to seek some other method. This should produce the output you seek:
for file in *; do
printf '%s ' "foo/bar/$file"
done; echo
I couldn't figure out how to get this done using only curly braces. I don't see a way to achieve this either, so unless someone more clever than I can figure out a way I'd say it's not possible.
As an alternative
Sample Data
$ tree
.
|-- dir1
| |-- file1
| `-- file2
`-- dir2
|-- file1
`-- file2
Examples
$ seq 2 | xargs -i{} echo dir{}/file{}
dir1/file1
dir2/file2
This can be put into a command like this:
$ echo $(seq 2 | xargs -i{} echo dir{}/file{})
dir1/file1 dir2/file2
or this:
$ ls $(seq 2 | xargs -i{} echo dir{}/file{})
dir1/file1 dir2/file2
or this:
$ ls -l $(seq 2 | xargs -i{} echo dir{}/file{})
-rw-rw-r-- 1 saml saml 0 Sep 2 03:18 dir1/file1
-rw-rw-r-- 1 saml saml 0 Sep 2 03:31 dir2/file2
Why curly braces can't do this
If you look at your original example:
{foo,bar}Q{foo,bar}
The way this gets expanded is as follows:
fooQfoo fooQbar barQfoo barQbar
The mechanism that expanded this is called a Cartesian Product.
For example:
$ echo {A,B}{X,Y,Z}
AX AY AZ BX BY BZ
Or this:
$ echo {M,N}-{A,B}{X,Y,Z}
M-AX M-AY M-AZ M-BX M-BY M-BZ N-AX N-AY N-AZ N-BX N-BY N-BZ
There is no way to create a Cartesian Product that will result in:
fooQfoo barQbar
You're only option is to either resort to trickery such as this:
$ echo dir{1,2}/file{2,1}
dir1/file2 dir1/file1 dir2/file2 dir2/file1
And then put this into a Bash array:
$ a=(dir{1,2}/file{2,1})
$ echo ${a[@]:1:2}
dir1/file1 dir2/file2
The other option would be some "other method" such as the one I previously discussed above (using xargs
) for example.
References
Best Answer
In
bash
, the expansion of{word}
is{word}
when the word does not contain a range (..
) or a comma.In your case
word
happens to contain two brace expansions. These are expanded left to right, so you get, first{a.{c,d}} {b.{c,d}}
, and then{a.c} {a.d} {b.c} {b.d}
which is the final result.Note that the outside braces are not expanded since they do not contain a range or a comma. You would get a similar result with
@{a,b}.{c,d}@
.Had you tried
{{a,b},{c,d}}
you would have three expansions (the expansion is done in three steps, each expanding one set of braces). First the outer one into{a,b} {c,d}
and then the left intoa b {c,d}
, and finallya b c d
, which is the final result.See also: Why does "cat {foo}" not output foo, but "cat {foo,bar}" does?