I have a program that acts like a menu. It has an associative array called config
such as:
declare -A config=( [h]="?" [c]="?" [x]="?" [l]="?" [t]="?" [n]="?" )
In the main loop there's a check to see if all of the values have been configured, like:
if [ "${config[h]}" == "Y" ] && [ "${config[c]}" == "Y" ] && [ "${config[l]}" == "Y" ] && [ "${config[x]}" == "Y" ] && [ "${config[t]}" == "Y" ] && [ "${config[n]}" == "Y" ];
Now, when I finish one run, I unset and redeclare the array.
unset config; declare -A config=( [h]="?" [c]="?" [x]="?" [l]="?" [t]="?" [n]="?" )
However, the array doesn't seem to be re-declared properly. This is because when I configure just one of the values, [C]=Y
for example, the if
statement passes. I know for sure it does because the body of the if
statement changes some of the text color. I believe it's because the rest of the indices aren't actually set="?", so the if
statement is reduced to just [ "${config[c]}" == "Y"]
which is be true
. I know this since when I echo $config[@]
inside the body of the if
statement, sure enough I only see one "Y" instead of five. How do I get the array to redeclare properly?
EDIT
Thank you for your attention;
Values are set to Y after some user input. This part I'm very confident about and thus omitted from my question. They all follow this format:
read ch
if [ $ch == "Hosts" ]; then
while true; do
nano listHosts
echo -en "Commit this list of Hostnames? [Y|N to re-edit]: "
read yn
if [ $yn == "Y" ] || [ $yn == "y" ] || [ $yn == "yes" ]; then
break
elif [ $yn == "N" ] || [ $yn == "n" ] || [ $yn == "no" ]; then
continue
fi
done
config[h]="Y"
Regarding Kusalanda's comment, I checked for incorrect casing but it's consistently lower-case 'c'. As well, yes, I meant ${config[@]}
.
Declare -p sheds some light:
declare -a config='([0]="Y")'
How come -p says I used lowercase a? The calls are in this order:
unset config
declare -A config=( [h]="?" [c]="?" [x]="?" [l]="?" [t]="?" [n]="?" )
Then I set $ch="Commands" via read
;
elif [ $ch == "Commands" ]; then
while true; do
nano iSet
echo -en "Commit this list of commands? [Y|N to re-edit]: "
read yn
if [ $yn == "Y" ] || [ $yn == "y" ] || [ $yn == "yes" ]; then
break
elif [ $yn == "N" ] || [ $yn == "n" ] || [ $yn == "no" ]; then
continue
fi
done
config[c]="Y"
declare -p config
I tried to recreate in a smaller script as per Bodo's suggestion:
dec() {
declare -A config=( [h]="?", [c]="?" )
}
test() {
declare -p config
if [ "${config[h]}" == "Y" ] && [ "${config[c]}" == "Y" ]; then
echo "Yup"
fi
}
dec
config[h]="Y"; config[c]="Y"
unset config
dec
config[h]="Y"
test
And just like in my other script, the if
resolves to true:
declare -a config='([0]="Y")'
Yup
You have to declare the array as global in your functions using declare -g ...
. Otherwise the array will be a local variable in the function. See https://unix.stackexchange.com/a/136721/330217
See this modified script with some debug output
#! /bin/bash
# set -x
dec() {
# declare -A config=( [h]="?", [c]="?" )
declare -gA config=( [h]="?" [c]="?" )
echo dec: ${config[*]}
}
test() {
declare -p config
if [ "${config[h]}" == "Y" ] && [ "${config[c]}" == "Y" ]; then
echo "Yup"
else
echo "No"
fi
echo test: ${config[*]}
}
dec
echo 1: ${config[*]}
config[h]="Y"; config[c]="Y"
echo 2: ${config[*]}
test
unset config
dec
echo 3: ${config[*]}
config[h]="Y"
echo 4: ${config[*]}
test
The output is
$ ./script
dec: ? ?
1: ? ?
2: Y Y
declare -A config=([c]="Y" [h]="Y" )
Yup
test: Y Y
dec: ? ?
3: ? ?
4: ? Y
declare -A config=([c]="?" [h]="Y" )
No
test: ? Y
When I uncomment your line and comment my modified line, the output is
$ ./script
dec: ? ?,
1:
2: Y
declare -a config=([0]="Y")
Yup
test: Y
dec: ? ?,
3:
4: Y
declare -a config=([0]="Y")
Yup
test: Y
Copied from @ilkkachu's comment:
And of course, after the local declaration of the associative array falls out of scope, the assignment
config[h]="Y"
creates a regular array, where the index is interpreted in an arithmetic context, whereh
(recursively) expands the value of the variableh
, which probably isn't set and you get the empty string that evaluates to zero, so it'sconfig[0]
that gets set. And there's no error message sinceset -u
isn't in effect.
This can be seen in the output declare -a config=([0]="Y")
from the original script.
config[h]="Y"
creates a regular array, where the index is interpreted in an arithmetic context, where h
(recursively) expands the value of the variable h
, which probably isn't set and you get the empty string that evaluates to zero, so it's config[0]
that gets set. And there's no error message since set -u
isn't in effect. — Jul 25, 2019 at 15:08 External links referenced by this document: