7

In my script I have an unknown number of variables containing angles which I want to convert to vectors. I made the variable-names such that the angles of each 'parameter' are of the form: {parameter}_angle_{lat/perp} Hence, each parameter has a 'lat' and 'perp' angle variable. So what I want to do is to find all variables containing '_angle_lat', do some calculations on the values of these variables and create a new variable which contains the 'parameter'-name in it. for example:

export M0_angle_lat=4
export M1_angle_lat=70
export M1_angle_perp=8
export M0_angle_perp=90

# Now I want to use these values to calculate vectors
for varname in *_angle_lat
do
    # at first iteration it will get for example "varname=M0_angle_lat" in which case
    # I want it to do:
    M0_X=$(( $M0_angle_lat * $M0_angle_perp ))
    # The second iteration in case would then give "varname=M1_angle_lat" in which case
    # I want it to do:
    M1_X=$(( $M1_angle_lat * $M1_angle_perp ))
done

I hope it's clear what my goal is here. Thanks for the help!

4
  • 2
    bash really isn't the language for this type of programming. It does have arrays, but they aren't suitable for building up more complex data structures, and trying to write code using dynamically generated variable names is error-prone, at best. (Although bash 4.3 introduces a feature that makes it a little easier.) Commented Sep 10, 2014 at 12:32
  • The reason I use bash is because it's a small script that has to be called on a cluster and all the surrounding code and scripts are in bash too. Commented Sep 10, 2014 at 12:37
  • If you can rewrite your variables to be matchable by prefix then you can use the ${!prefix*} and ${!prefix@} expansions to get the names of variables that begin with prefix. Commented Sep 10, 2014 at 13:49
  • That's what @chepner also suggested in his answer. But the reason for having the 'parameter'-name as prefix is due to some other attributes of that parameter that also have to be defined and should be grouped. Though the accepted answer takes care of my problem without the need for changing the prefix. But thanks for the suggestions! Commented Sep 10, 2014 at 14:33

2 Answers 2

18

What you can do is use env to get a list of all variables and then iterate through them:

while IFS='=' read -r name value ; do
  if [[ $name == *'_angle_lat'* ]]; then
    echo "$name" ${!name}
    prefix=${name%%_*} # delete longest match from back (everything after first _)
    angle_lat="${prefix}_angle_lat"
    angle_perp="${prefix}_angle_perp"
    result="${prefix}_X"
    declare "${result}=$(( ${!angle_lat} * ${!angle_perp} ))"       
  fi
done < <(env)
Sign up to request clarification or add additional context in comments.

3 Comments

This seems to be a huge step forward. But in this case I have $var being the whole string: M0_angle_lat=4. How do I extract the 'parameter' from this (M0 in this case)? And how do I use it to create the variable M0_X=$(( $M0_angle_lat * $M0_angle_perp ))?
I see what this code tries to do, but it doesn't seem to work. I just checked the version of bash running on the cluster and it turns out it's an old one: GNU bash, version 3.2.25(1)-release (x86_64-redhat-linux-gnu) Copyright (C) 2005 Free Software Foundation, Inc.
It turned out that my previous was no problem with bash, but purely a few small errors in the code which are now fixed!
1

This code requires bash 4.3, to use the named references created by declare -n.

This also requires you to rename your variables slightly.

angle_lat_M0=4
angle_lat_M1=70
angle_perp_M1=8
angle_perp_M0=90

# Now I want to use these values to calculate vectors
for varname in ${!angle_lat*}
do
    # Ref to angle_lat_*
    declare -n lat=$varname
    # Ref to angle_perp_*
    declare -n perp=${varname/_lat_/_perp_}
    # Ref to X_*
    declare -n x=${varname/angle_lat_/X_}

    x=$((lat * perp))
done

echo $X_M0
echo $X_M1

Prior to 4.3, you need some extra tricks to work varname. (Actually, it's not as bad as I thought it would be.)

angle_lat_M0=4
angle_lat_M1=70
angle_perp_M1=8
angle_perp_M0=90

# Now I want to use these values to calculate vectors
for varname in ${!angle_lat*}
do
    tag=${varname#angle_lat_}  # M0, M1, etc
    lat=${!varname}
    perp_name=angle_perp_$tag
    perp=${!perp_name}
    x=$((lat * perp))

    declare "X_$tag=$x"
done

echo $X_M0
echo $X_M1

Even simpler, and it should work for all versions of bash (possibly excluding some very old versions, but 3.2 at least is supported). It's simpler mainly because it forgoes trying to iterate over a set of similar variable names.

lats=( $M0_angle_lat $M1_angle_lat )
perps=( $M0_angle_perp $M1_angle_lat )
declare -a x

for i in "${!lats[@]}"; do
    x+=(${lats[i]} * ${perps[i]})
done

M0_X=${x[0]}
M1_X=${x[1]}
# or
for i in "${!x[@]}"; do
    declare "M${i}_X"=${x[i]}
done

Comments

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.