1

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;

  1. 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"
    
  2. Regarding Kusalanda's comment, I checked for incorrect casing but it's consistently lower-case 'c'. As well, yes, I meant ${config[@]}.

  3. 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

  • 1
    (a) I don't see anything being set to Y, and (b) $config[@] ... Did you mean "${config[@]}"? – muru Jul 25 at 14:01
  • 1
    Do you mean [c]="Y" rather than [C]=Y? Also, did you mean you printed ${config[@]} rather than $config[@]? – Kusalananda Jul 25 at 14:01
  • 1
    Please show a minimal complete script to reproduce the problem. Instead of showing a menu and requiring manual input you can simply set hard-coded values in your script. – Bodo Jul 25 at 14:02
  • Are you setting and unsetting this array outside the script, from the calling shell? Arrays can't be exported, iirc. – muru Jul 25 at 14:17
  • 1
    If you you declare a variable inside a function, it makes that variable local to the function. It is an issue in the test code that you show, but we don't know whether you have the same issue in your real code. – Kusalananda Jul 25 at 14:52
2

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, 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.

This can be seen in the output declare -a config=([0]="Y") from the original script.

  • Using the -g switch fixed this problem! Thank you. – KuboMD Jul 25 at 15:05
  • 2
    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, 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. – ilkkachu Jul 25 at 15:08
  • @ilkkachu thank you for the in-depth insight! – KuboMD Jul 25 at 16:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.