2

I want to assign the value of an array dynamically, and when I change the variable the array changes automatically.

var=5  
arr=( $\[$var-1\] $var $\[$var+1\] )  
echo ${arr\[@\]}  
var=10  
echo ${arr\[@\]}

result

4 5 6
4 5 6 

I wanted

4 5 6 
9 10 11
adamfingol
  • 23
  • 3
  • 2
    Shell arrays do not work this way. In fact, what language do you know that works this way? – axiac Dec 28 '22 at 14:59
  • 3
    Variables just don't work like that. What you can do is instead of assigning var=10 directly, write a function which assigns to var and updates the array. – Verpous Dec 28 '22 at 15:00
  • 1
    Do you really have all those backslashes in your code, or did the SO Stacks editor add them? – Barmar Dec 28 '22 at 15:44
  • ***I don't see why this should be closed. The man is asking a valid question.*** adamfingol: Just use a language that has support for references / pointers. Declare an array of pointers and forget about doing that in bash - there is no need for it in shell scripting, it's too advanced. That may sound condescending, but shell scripting is supposed to be simple. There weren't even any associative arrays in bash for ages (20 years) after the initial release. – Ярослав Рахматуллин Jan 06 '23 at 09:36

3 Answers3

2

Try something like this:

v1=abc
v2=efd
v3=ghj

arr=( v1 v2 v3 )

for i in ${arr[@]}; { echo ${!i}; }
abc
efd
ghj

Now lets change values of those vars:

v1=123
v2=456
v3=789

$ for i in ${arr[@]}; { echo ${!i}; } 
123
456
789
Ivan
  • 6,188
  • 1
  • 16
  • 23
  • Where is the documentation for this loop syntax? – Paul Hodges Dec 28 '22 at 20:36
  • It's an undocumented hack for backwards compatibility. I don't recommend it. c.f. [this post](https://stackoverflow.com/questions/63247449/alternative-for-loop-construct) - but thanks for pointing it out so I'll know next time I see it, lol... the above works fine with *standard* syntax using `do`/`done`. – Paul Hodges Dec 28 '22 at 21:13
  • @PaulHodges it's here [indirect expansion](https://www.gnu.org/software/bash/manual/bash.html#Shell-Parameter-Expansion) – Ivan Jan 04 '23 at 06:00
  • No, I get indirect expansion - I meant replacing `do/done` with curlies. see the link I posted above to https://stackoverflow.com/questions/63247449/alternative-for-loop-construct – Paul Hodges Jan 04 '23 at 14:13
  • @PaulHodges I't looks like a valid shorthand to me. you can do stuff like that in zsh even without the brackets. Why don't you recommend it? just because the current maintainer says so? what about the guy who implemented it - was he wrong? What's the harm in using this construct? Even shellcheck doesn't mind it (i'm surprised)! Do you generally just hate everything that's fun about shell scripting? What's your opinion on omitting `in "$@"` from a for loop? Also bad? https://unix.stackexchange.com/a/417296/23421 Please state your reasons in a comment, I am genuinely curious. – Ярослав Рахматуллин Jan 06 '23 at 09:19
  • 1
    @Ivan Wow, I didn't know there was a shorthand like that in bash. Oh man... so many times have I typed `; do .. done` and thought to myself with despair ***what kludgy syntax***... I've even considered switching to zsh because of this tiny specific feature! You are a true jedi! ***AND*** this was answered on my BIRTHDAY! The stars have a aligned :D Aquarius is in the seventh house! This is a sign. hahahah – Ярослав Рахматуллин Jan 06 '23 at 09:29
  • 1
    Happy birthday @ЯрославРахматуллин!) Yes it's pretty handy but as @PaulHodges pointed [here](https://stackoverflow.com/questions/63247449/alternative-for-loop-construct) it won't work in `while/until` loops unfortunately. – Ivan Jan 06 '23 at 09:37
1

Elaborating Ivan's trick and applying get/set "method"-style functions -

[P2759474@sdp-bastion ~]$ cat tst
#! /bin/bash

arr=( v1 v2 v3 )
v1(){ (($1))&& var=$(($1+1)); echo $((var-1)); }
v2(){ (($1))&& var=$1;        echo $var;       }
v3(){ (($1))&& var=$(($1-1)); echo $((var+1)); }

var=5
for i in ${arr[@]}; { printf "%s=" $i; $i; }

v1 14
for i in ${arr[@]}; { printf "%s=" $i; $i; }

[P2759474@sdp-bastion ~]$ ./tst
v1=4
v2=5
v3=6
14
v1=14
v2=15
v3=16
Paul Hodges
  • 13,382
  • 1
  • 17
  • 36
0

While such automatic updates are perfectly common in spreadsheet applications, Bash doesn’t work that way. If you want to recalculate a few values based on formulas, you have to explicitly tell Bash to do so. The example below generates the outputs that you expect and defines a function called recalculate that you need to call after changes to the formulas or the formulas’ input variables. The rest of the trick is based around how integer evaluation works in Bash.

recalculate() {
  local -n source="$1" target="$2"
  target=("${source[@]}")
}

formulas=('var - 1' 'var' 'var + 1')
declare -ai arr

var=5
recalculate formulas arr
echo "${arr[@]}"          # 4 5 6

var=10
recalculate formulas arr
echo "${arr[@]}"          # 9 10 11

(It would be awesome if Bash had an additional pseudo-signal for the trap command, say assignment, which could work like trap 'echo "variable ${1} set to ${!1}"' assignment, but AFAIK, there is no such functionality (plus no separate argument handling in trap); hence the strikethrough. Without that kind of functionality, a function like recalculate might be the closest you can get to the updates you asked for.)

A slightly more elaborate version of recalculate could also (1) handle sparse arrays of formulas correctly, i.e. guarantee to store results under the same indices under which the corresponding formulas were found, and (2) introduce a “reserved” variable name, say index, which can occur in the formulas with an obvious meaning. Just for fun, “because we can”:

recalculate() {
  local -n source="$1" target="$2"
  local -i index
  target=()
  for index in "${!source[@]}"; do
    target[index]="${source[index]}"
  done
}

formulas[1]='index * var - 1'
formulas[3]='index * var'
formulas[5]='index * var + 1'
declare -ai arr

var=5
recalculate formulas arr
echo "${arr[@]@A}"  # declare -ai arr=([1]="4" [3]="15" [5]="26")

var=10
recalculate formulas arr
echo "${arr[@]@A}"  # declare -ai arr=([1]="9" [3]="30" [5]="51")
Andrej Podzimek
  • 2,409
  • 9
  • 12