5

In a bash script, I have a locale in a variable like so

locale=fr_ma

I also have an associative array like this

declare -A new_loc_map
new_loc[fr_ma]=en_ma
new_loc[el_gr]=en_gr
new_loc[sl_si]=en_si

I want to check if new_loc element with the key ${locale} exists

I thought this should work but it doesn't:

if [[ -v "${new_loc[${locale}]}" ]]
    then
        echo -e "${locale} has a new_loc"
    fi
fi

any ideas on how otherwise I can test for this?

3 Answers 3

8

-v takes an (indexed) name as its argument, since you are trying to determine if the expansion makes sense in the first place.

if [[ -v new_loc[$locale] ]]; then
    echo "Locale ${locale} now maps to ${new_loc[$locale]}"
fi

Word of warning

While the BASH manual page describes -v for [[ and test, reliable results are returned from [[ only. Consider this (Bash 4.4):

$ [ -v "$a[1]" ] && echo true
$ a[1]=''
$ [ -v "$a[1]" ] && echo true
$ declare -p a
declare -a a=([1]="")
$ [ -v $a[1] ] && echo true
$ [[ -v $a[1] ]] && echo true
$ [[ -v a[1] ]] && echo true
true
$ [[ -v a[0] ]] && echo true
$

On newer versions of Bash such as 5.2.37, all variations work fine, including:

$ test -v a[1] && echo true
true
Sign up to request clarification or add additional context in comments.

2 Comments

What is your $BASH_VERSION?
You need bash 4.3 or later for -v to work with an array index.
7

For older verions of bash (looks like [[ -v array[index] ]] was introduced in version 4.3), you can use the ${var-word} form to test is a variable has been set:

$ zz="$RANDOM$RANDOM$RANDOM"
$ echo $zz
270502100415054
$ declare -a name
$ locale=foo

$ [[ ${name[$locale]-$zz} = "$zz" ]] && echo var is unset || echo var has a value
var is unset

$ name[$locale]=""
$ [[ ${name[$locale]-$zz} = "$zz" ]] && echo var is unset || echo var has a value
var has a value

$ [[ ${name[$locale]:-$zz} = "$zz" ]] && echo var is unset or empty || echo var has a value
var is unset or empty

The tricky part is devising a $zz string that won't appear as actual data in your array.


Much better suggestion from @chepner:

if [[ -z "${name[$locale]+unset}" ]]; then 
    echo "no name for $locale"
else 
    echo "name for $locale is ${name[$locale]}"
fi 

4 Comments

Use ${name[$locale]+unset}, which will produce a non-empty string if$locale is a key, or an empty string if not.
How do I put this condition inside an if statement?
if [[ -z "${name[$locale]+unset}" ]]; then echo "no name for $locale"; else echo "name for $locale is ${name[$locale]}"; fi
Actually I think this is not correct in general (like in Perl if ($h{x}) vs. if (exists($h{x})) if the element at an index may be the empty string.
2

I managed to solve the problem by checking if the variable is not an empty string instead.

Example:

locale=fr_ma
declare -A new_loc
new_loc[fr_ma]=en_ma
new_loc[el_gr]=en_gr

if test "${new_loc[$locale]}"; then
    echo "Locale ${locale} now maps to ${new_loc[$locale]}"
fi

Output:

Locale fr_ma now maps to en_ma

1 Comment

-n is the same as ! -z

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.