First of all sorry because my english may be not good. I want to use a variable to index an element in an array or use the same variable to index all the elements. For example:
...
var1="1"
var2="*"
array=(one two three for five)
for elem in ${array[$var1]}
do
echo $elem
done
When I use var1 to index in ${array[$var1]} it works correctly, but if I use var2 doesn't work correctly, I get this error:
./ed.sh line XXX *: syntax error: operand expected (error token is "*")
I'm pretty sure that the error is related with the * wildcard expansion, but I didn't find an answer that help me to solve this problem. So, how can I do it?
Similar to other programming languages, Bash array elements can be accessed using index number starts from 0 then 1,2,3…n. This will work with the associative array which index numbers are numeric. To print all elements of an Array using @ or * instead of the specific index number.
Assigning values to associative array variable elements can be done by using the assignment statement in which the array is named, the index value is specified and the corresponding element value is assigned.
How to Echo a Bash Array? To echo an array, use the format echo ${Array[0]}. Array is your array name, and 0 is the index or the key if you are echoing an associative array. You can also use @ or * symbols instead of an index to print the entire array.
*
and @
are not considered regular elements in the array. They are not listed when iterating keys, and are not considered when expanding indirectly through index variables.
The bash source code has a function chk_atstar
that checks whether [@]
or [*]
is being used, and you can see that it's done literally and not through any expansion:
else if (valid_array_reference (name, 0))
{
temp1 = mbschr (name, '[');
if (temp1 && temp1[1] == '@' && temp1[2] == ']')
{
If you really want to do this, you can go through variable indirection:
arr=(one two three)
index='*'
var="arr[$index]"
echo "${!var}"
though you may be better off not trying to treat these special array access modes as array elements.
I don't recommend this, but for completeness you can get this to work by cheating with the expansion order using eval
:
eval items=\${array[$var2]}
for elem in $items
do
echo $elem
done
There are issues with this. eval
is generally pronounced "evil" because there can be security implications in running code from a variable. There is usually a better way to do the job than using eval
. In this case you should give some thought to the design.
There is also an issue if an element contains embedded whitespace. Add:
array+=('at the end')
After the array declaration and you'll see what I mean.
EDIT: After some deliberation, here is a way to do it without eval
, and it supports embedded spaces or tabs (but not embedded newlines). Pretty it is not:
display_it() {
if [[ $1 = '*' ]]; then
oldIFS="$IFS"
IFS=$'\n'
echo "${array[*]}"
IFS="$oldIFS"
else
echo "${array[$1]}"
fi
}
var1="1"
var2="*"
array=(one two three for five)
array+=('at the end')
while read -r elem
do
echo $elem
done < <(display_it "$var2")
Displays:
one
two
three
for
five
at the end
At the end of the loop you will see process substitution where I call the function display_it
. Each item read is separated by a newline, hence the swapping of the Internal Field Separator (IFS
) in the function.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With