2

I have an array of variables. I want to check if the variables have a value using the for loop.

I am getting the values into loop but the if condition is failing

function check {
    arr=("$@")
    for var in "${arr[@]}"; do
        if [ -z $var ] ; then
            echo $var "is not available"
        else
            echo $var "is available"
        fi
    done
}

name="abc"
city="xyz"
arr=(name city state country)
check ${arr[@]}

For the above I am getting all as available

Expected output is

name is available
city is available
state is not available
country is not available
pradeep
  • 413
  • 2
  • 7
  • 20
  • `var` in your loop takes on the values _name_, _city_, _state_ and _country_. [ -z ... ] tests whether the length of the argument is zero. None of those words has zero length, so the `else` branch is taken every time. – user1934428 Feb 28 '20 at 11:42

2 Answers2

4

This is the correct syntax for your task

if [ -z "${!var}" ] ; then
    echo $var "is not available"
else
    echo $var "is available"
fi

Explanation, this method uses an indirect variable expansion, this construction ${!var} will expand as value of variable which name is in $var.

Changed check function a bit

check () {
    for var in "$@"; do
        [[ "${!var}" ]] && not= || not="not "
        echo "$var is ${not}available"
    done
}

And another variant using declare

check () {
    for var in "$@"; do
        declare -p $var &> /dev/null && not= || not="not "
        echo "$var is ${not}available"
    done
}

From declare help

$ declare --help
declare: declare [-aAfFgilnrtux] [-p] [name[=value] ...]
    Set variable values and attributes.
    Declare variables and give them attributes.  If no NAMEs are given,
    display the attributes and values of all variables.
    ...
    -p  display the attributes and value of each NAME
    ...

Actually all vars can be checked at once using this

check () {
    declare -p $@ 2>&1 | sed 's/.* \(.*\)=.*/\1 is available/;s/.*declare: \(.*\):.*/\1 is not available/'
}
Ivan
  • 6,188
  • 1
  • 16
  • 23
0

While indirection is a possible solution, it is not really recommended to use. A safer way would be to use an associative array:

function check {
    eval "declare -A arr="${1#*=}
    shift
    for var in "$@"; do
        if [ -z "${arr[$var]}" ] ; then
            echo $var "is not available"
        else
            echo $var "is available"
        fi
    done
}

declare -A list
list[name]="abc"
list[city]="xyz"

check "$(declare -p list)" name city state country

This returns:

name is available
city is available
state is not available
country is not available

The following question was used to create this answer: How to rename an associative array in Bash?

kvantour
  • 25,269
  • 4
  • 47
  • 72