You're right, b="${a[@]}"
doesn't create an array in Bash. Without the parenthesis, that's a scalar assignment, regardless of what there is on the right-hand side. So even though you used the @
indexing to ask for all the array items separately the assignment forces them to a single string. Meaning b="${a[@]}"
is quite like b="${a[*]}"
except that it always uses a space as a joiner, instead of the first character of $IFS
.
E.g.
$ a=("foo bar" doo);
$ IFS=:;
$ b="${a[@]}"
$ c="${a[*]}"
$ typeset -p b c
declare -- b="foo bar doo"
declare -- c="foo bar:doo"
To get an array, you need to add the parenthesis, i.e. b=( "${a[@]}" )
would copy a simple array like the one you have. I say "simple", since array indexes can be non-contiguous, and the expansion there would lose the indexes, with the assignment creating new, contiguous ones starting from 0
.
$ d[5]=xxx d[8]=yyzz
$ e=( "${d[@]}" )
$ typeset -p d e
declare -a d='([5]="xxx" [8]="yyzz")'
declare -a e='([0]="xxx" [1]="yyzz")'
If you want a copy that keeps the indexes, it's likely safest to just use a loop. In general, variables also have attributes like the export and integer attributes, and you'll have to re-set them manually too. There's no simple and straightforward way to copy a variable in full.
There is the "${var@A}"
expansion in newer versions of Bash that gives the declare
command to recreate the variable, but it contains the original name, so can't immediately be used for copying. I suppose you could do something like this to copy d
into f
...
$ d[5]=xxx d[8]=yyzz
$ d_repr="${d[@]@A}"
$ eval "declare -a f=${d_repr#declare -a d=}"
$ typeset -p d f
declare -a d=([5]="xxx" [8]="yyzz")
declare -a f=([5]="xxx" [8]="yyzz")
...but I would be wary of doing that in real life and in any case, that requires taking into account the attributes of the original variable. E.g. an integer array g
would print as declare -ai g=...
.
Note that echo
joins the arguments it gets with spaces, so echo ${a[@]}
or echo "${a[@]}"
hides where the separation between the array items actually is. And when the scalar assignment joins with spaces, and echo
joins with spaces, the result is that the output is the same in both these cases.
That makes echo
really bad for looking into things like this. It's better to use e.g. printf
here, e.g. this makes it clearer that b
is just a single element.
$ unset a b
$ a=("foo bar" doo)
$ b="${a[@]}"
$ printf "<%s>\n" "${a[@]}"
<foo bar>
<doo>
$ printf "<%s>\n" "${b[@]}"
<foo bar doo>
Or use typeset -p
/ declare -p
.
Even in zsh, where $a
expands it as an array (with distinct items separated), b=$a
still joins to a string, so you need to know what type of a variable you're handling. (b=$a[@]
is the same, and zsh uses the first character of IFS
for both @
and *
, a minor difference from Bash.)
echo $anything
(without quotes) is almost always a bug that should have been writtenecho "$anything"
. The 2 statements are not equivalent, see mywiki.wooledge.org/Quotes.find
since it doesn't support double-asterisk wildcard **" -find
doesn't need to support double-asterisk wildcard to look in sub-directories since it does that by default. By the way, you might want to addshopt -s nullglob
to the top of your script soFILES
doesn't literally containfoo/**/*.suffix
if/when there are no.suffix
files underfoo
.