In double quotes, only a few things are expanded: parameter and command substitution, most importantly, but no filenames (and no brace expansion), so *
within double quotes is always literal.
Then, on the right-hand side of an assignment, there is also no filename and brace expansion, so var=*
will put a literal *
into var
, even without quotes. Your last example "works" only because you don't quote the expansion of $var
, but it contains literally /home/username/A*
.
The way to do it is to use an array:
fnames=(/home/username/{A,B,C}*)
This will perform both brace and filename expansion, and the array elements will contain one filename each, properly escaping spaces and glob characters. Access them with proper quoting:
echo "${fnames[0]}"
gives you the first filename, for example.
If you supply the pattern as a parameter to a script, there's probably no way around using eval
:
pattern=$1
eval fnames=("$pattern")
This comes with the usual warnings about eval
. If you supply as a pattern the following:
pattern='x); echo pwnd #'
eval
will expand the line to
fnames=(x); echo pwnd #)
and actually run the injected command, which could be less friendly than just an echo.
Sadly, the robust method
eval "$(printf 'fnames=(%q)' "$pattern")"
doesn't work, as it prevents expansion.
To avoid this, I'd recommend rewriting your script to take multiple arguments and let the shell do the expansion:
./yourscript path/to/dir/{A,B}*
and in the script something like
for file; do <something>; done
(which is the same as for file in "$@"; do <something>; done
). Now you have the benefits of expansion being taken care of by the shell, without the drawbacks of eval
.