If you weren't working with just math I'd suggest the only other safe/portable method I know of one that doesn't require very stringent testing:
var1=string1
export "$var1=string2"
echo "$var1"
echo "$string1"
OUTPUT
string1
string2
But you are just working with math, and so you're problem is that you are echo
ing the variable's value into a subshell when you should be concurrently defining it and evaluating it in the current shell.
i=0 ; until [ $((i=$i+1)) -ge 3 ]
do echo "\$x_$i = $((x_$i=3+$i))"
done
echo $x_1 $x_2
OUTPUT
$x_1 = 4
$x_2 = 5
4 5
So what you're doing is just math. The interesting thing about shell arithmetic is that you can use it to safely twice evaluate a variable in the current shell - no subshells.
POSIX has this to say about it:
Arithmetic expansion provides a mechanism for evaluating an arithmetic expression and substituting its value. The format for arithmetic expansion shall be as follows:
$((expression))
The expression shall be treated as if it were in double-quotes, except that a double-quote inside the expression is not treated specially. The shell shall expand all tokens in the expression for parameter expansion, command substitution, and quote removal.
Perhaps more interestingly, it also has this to say:
All changes to variables in an arithmetic expression shall be in effect after the arithmetic expansion, as in the parameter expansion "${x=value}"
.
If the shell variable x
contains a value that forms a valid integer constant, optionally including a leading plus or minus sign, then the arithmetic expansions "$((x))"
and "$(($x))"
shall return the same value.
So if a variable is defined within the context of an arithmetic substitution, that definition persists in the current environment. Like:
echo $((x=1)); echo $x
1
1
But if you add to that the other parts - specifically that the shell shall expand all tokens and that "$((x))"
and "$(($x))"
shall return the same value. You might be able to see how the above until
loop works.
So:
$((x_ #just a string
$i #integer value
= #assignment operator
3 + #addition
$i )) #same integer value
You see the shell has to expand the $i
in both contexts - that of the variable shell token as a string before the assignment operator, and that of the integer value on which it performs the addition operation, the sum of which is assigned to x_$i
.
Arrays can be handy, but, not counting one form, they are not portable and all come with their own implementation-specific snaffus. And besides - this is the answer to the question you asked.
This is why the following function works:
defv() {
[ -n "${1##*[!0-9]*}" ] && # verify $1 is numeric only
v=x_$1 && # redefine caller loop var
: $((x_$1=5+$1)) # : do nothing but expand arg
}
for v in 1 2 3 4 5 # init array
do defv $v # defv() $indirection
echo "$v = $(($v))" # \$x_$indirection = $x_$indirection
done
OUTPUT
x_1 = 6
x_2 = 7
x_3 = 8
x_4 = 9
x_5 = 10
Your functions:
check_1()
{
check_2 x_$1 $1
}
check_2()
{
: $(($1=3+$2))
}
for((i=1; i<=2;i++))
do
check_1 $i
tmp=x_$i
echo ${!tmp}
done
OUTPUT
4
5