3

I'm trying to read from Bash variables for which I know name suffixes, but I want to iterate through the prefixes.

I give an example below:

var1_name="variable1"
var1_size="2"
var2_name="variable2"
var2_size="3"
vars=(var1 var2)

for v in "${vars[@]}"
do
    echo $v_name
    echo $v_size
done

and I'd want the output to look like follows:

variable1
2
variable2
3

Is there any to do this with Bash? I have tried with eval and associative arrays, but I still can't find a way to examine an already defined variable.

Paulie-C
  • 1,674
  • 1
  • 13
  • 29
Ofri Rips
  • 153
  • 1
  • 10

2 Answers2

3

Below works for me. You need to construct the variable first and then evaluate it using exclamation.

var1_name="variable1"
var1_size="2"
var2_name="variable2"
var2_size="3"
vars=("var1" "var2")

for v in "${vars[@]}"
do
    name=${v}_name
    size=${v}_size
    echo ${!name}
    echo ${!size}
done

O/P

variable1
2
variable2
3
auhuman
  • 962
  • 2
  • 13
  • 34
2

auhuman's helpful answer solves your problem exactly as asked.

Here's an alternative that doesn't require you to specify the name prefixes in an array first, assuming that all variables of interest have names with the same prefix, such as var:

for name in "${!var@}"  # Loop over all variables whose names start with 'var'.
do
    [[ $name =~ ^var[0-9]+_(name|size)$ ]] || continue # Ignore unrelated names.
    echo "${!name}" # Use variable indirection to print the variable's value. 
done
  • The code uses two forms of Bash variable indirection, all based on syntax ${!...}:

    • "${!var@}" is the literal-based list form: it expands to the list (array) of the names of all defined variables whose names have literal prefix var (whose names start with var), if any. Since only a literal is allowed to precede @, using a pattern such as
      "${!var[0-9]_@}" for more sophisticated matching does not work, unfortunately.

      • Example: for varName in "${!HOST@}"; do echo "$varName"; done
        yields HOSTNAME and HOSTTYPE, for instance.

      • The related ${!var*} syntax (without double quotes) seems to work the same.
        (Curiously, the man page claims that the 1st char. of the $IFS value is used to concatenate the matching names, which would only work as expected in a for loop if that 1st char. is a whitespace char. (which is true of the $IFS default value, which starts with a space), but in practice a space is always used, irrespective of the value of $IFS; verify with (IFS=@; echo ${!HOST*}))

    • ${!name}, the variable-based scalar form, expands to the value of the variable whose name is stored as the value of $name.

      • Example: var='value'; name='var'; echo ${!name} yields value.

      • Note: With a trick (not used above), you can use indirection to get an array's values too, namely by appending the all-indices index expression [@] to the name of the variable to reference indirectly; e.g.:
        arr=( one two three ); name='arr[@]'; echo "${!name}" yields one two three; accessing a specific element this way (e.g., name='arr[1]') works too, but extracting a range of elements does not (e.g., name='arr[@]: 0:2').

    • There is also an array-index list form - not used above - which expands to the list (array) of indices / keys of a given array / associative array.

      • Example: arr=( one two three ); echo "${!arr[@]}"
        yields 0 1 2, the implicit sequential indices of the array.

      • It works analogously with explicitly assigned indices (e.g.,
        arr=( [10]=one two three ); echo "${!arr[@]}") or, in Bash v4+, with associative arrays (although, as is the nature of associative arrays, the keys are not guaranteed to be listed in the order they were defined; e.g.,
        declare -A aarr=( [one]=1 [two]=2 ); echo "${!aarr[@]}").

  • [[ $v =~ ^var[0-9]+_(name|size)$ ]] uses the =~ operator to match the variable name at hand against the extended regular expression on the RHS to determine if it is a name of interest.

Community
  • 1
  • 1
mklement0
  • 382,024
  • 64
  • 607
  • 775
  • 1
    I actually don't use the names var1, var2... It's a bit different, but it could be very useful for similar things I'm doing in that script, Thanks! – Ofri Rips Apr 09 '17 at 13:28
  • @OfriRips: Understood, and thanks. I've made it clearer that my answer assumes a shared name prefix (and I've added an overview of variable indirection). Thanks for coming back and accepting auhuman's answer. Please also consider revisiting your old questions to see if answers there deserve being accepted. – mklement0 Apr 09 '17 at 16:59