Why arguments in braces do not expand in this case?

For example, I want to put some file names inside braces for expansion like this:

$ ls
blue_texture  blue_waves  green_light
$ ls -m | tr -d ' n'
blue_texture,blue_waves,green_light 
$ echo foo/bar/{`ls -m | tr -d ' n'`}
foo/bar/{blue_texture,blue_waves,green_light}

My question why doesn’t brace expansion work here? I expected it to work as following:

$ echo foo/bar/{blue_texture,blue_waves,green_light}
foo/bar/blue_texture foo/bar/blue_waves foo/bar/green_light
Asked By: Maxim

||

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
Answered By: Chris Down

With zsh:

files=(*)
echo foo/bar/$^l

(${^array} turns on brace-expansion style expansion for the array).

Or:

echo ./*(:s:.:foo/bar:)

Applies a history-expansion style substitution to filename expansion.

With any shell

printf 'foo/bar/%sn' *

Or:

set -- *
for i do
  set -- "$@" "foo/bar/$i"
  shift
done
echo "$@"

(note that echo in most shells performs some transformations on the arguments it gets so is better avoided for arbitrary input).

Answered By: Stéphane Chazelas
Categories: Answers Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.