Greg Wooledge wrote:
Also I think you are completely misrepresenting the dynamic variable
scope system that bash uses. Variables are not just global or local.
There's an entire stack of them. When you reference a
variable (let's say i) inside a function, bash searches up
through the call stack looking for a variable named
i until it finds one.
----
So where in the manpage is the behavior documented -- I'd like
to reread it to ensure I don't have any misconceptions.
The reason being is that out of 4 tests below,
2 fail. It appears test 2 failing is a failure of the
dynamic scoping system.
Test 3 was meant to be an extension, as I have some
code where I change a member in a hash, and try to compare
the final result with a known correct value, but as in test-2
all the other members of the hash disappear
Notice that 'parts' is defined at the global level as an array.
in tst0 & tst1, I locally change the value of IFS, and
call "assignparts" that splits the value "$ip" based on
the "localized" values of IFS -- tst0 + tst1 work as I'd
expect with dynamic scoping. Since parts was declared 'global',
the different splitting caused by differing 'IFS' values is
reflected in the global.
However, in tst3, I only change 1 member in the parts
array, via "parts[1]=222".
before the assignment, parts=(1 .2 .3 .4)
but after the assignment "parts[1]=222",
the entire array was wiped.
I.e. test output was:
Case 2 got/Expected:
"222"
"1\ 222\ .3\ .4"
instead of it only replacing the 2nd member of parts,
the entire array was wiped.
The same happens if parts is a 'hash' -- trying
to only replace 1 member of the hash results in the
entire hash being wiped.
I don't see how this is correct function for
"dynamically scoped" variables -- which is why
I need to reread (or read) the manpage descriptions of
how dynamic vars are *supposed* to work, as they don't
seem to be working by "unwinding" the call stack and using
the last definition of 'parts' (which is at the global level).
test 3 --- was supposed to check the values
of the hash field-by-field, but first I thought to check
them when printed out in a specific order as 2 identical
strings. The strings look identical, yet don't compare equal.
so it seemed pointless to try to break down test 3's output
into a field-by-field comparison when I couldn't even get
the identical strings that contained the fields to compare.
tests 2 & 3 work backward from a program where I try
to change 1 value in a hash (vs. the array in test2), but
end up with the local assignments of values->[keys] not
being propagated, at all, to the global frame where the has
is declared.
I.e. these examples of dynamic variable declarations and usage
don't seem to work as I understand the concept, but there is
nothing about bash's 'dynamic variables' in the manpage.
-l
-----
#!/bin/bash
shopt -s expand_aliases ; alias my=declare
alias array='my -a' int='my -i'
ip=10.20.30.40
array parts=()
array answers=( '10 20 30 40' '1 .2 .3 .4' '1 222 .3 .4'
'192.168.0.1/24::{ [ipaddr]="192.168.0.1/24" [address]="192.168.0.1"
[prefixlen]="24" ; }'
)
array addr_fields=(ipaddr address prefixlen)
assignparts() { parts=( $ip ) ; }
tst0 () { my IFS=. ; assignparts ; echo -E "${parts[@]}"; }
tst1 () { my IFS=0 ; assignparts ; echo -E "${parts[@]}"; }
tst2 () { parts[1]=222; echo -E "${parts[@]}" ; }
tst3 () {
my ipaddr="$1"; shift;
int status=0
my pat='^([^/]+)/([0-9]+)\s*$'
my address prefixlen
if [[ ! $ipaddr =~ $pat ]]; then
echo >&2 "Error in ip/netsize format: \"$ipaddr\""
status=1
else
address=${BASH_REMATCH[1]}
prefixlen=${BASH_REMATCH[2]}
my out=""
for flds in "${addr_fields[@]}"; do
out+="[$flds]=\"${!flds}\" "
done
printf "{ %s; }" "$out"
fi
return $status
}
int passno=0 tests=0
my fmt="Case %d got/Expected:\n \"%q\"\n \"%q\"\n"
testor () {
int tstno
my out=""
for tstno in {0..3}; do
tests+=1
exp="${answers[tstno]}"
if [[ $exp =~ :: ]]; then
my args="${exp%::*}"
exp="${exp#*::}"
out="$(tst$tstno $args)"
else
out="$(tst$tstno)"
fi
if [[ $out != $exp ]]; then
printf >&2 "$fmt" "$tstno" "$out" "$exp"
continue
fi
passno+=1
done
}
testor
echo "Passed $passno/$tests tests."
----------------
output:
Case 2 got/Expected:
"222"
"1\ 222\ .3\ .4"
Case 3 got/Expected:
"\{\ \[ipaddr\]=\"192.168.0.1/24\"\ \[address\]=\"192.168.0.1\"\
\[prefixlen\]=\"24\"\ \;\ \}"
"\{\ \[ipaddr\]=\"192.168.0.1/24\"\ \[address\]=\"192.168.0.1\"\
\[prefixlen\]=\"24\"\ \;\ \}"
Passed 2/4 tests.
The outputs for case 3 look identical -- I was using %s to print
them out, but switched to "%q", to ensure no hidden chars...
case 2 --
??? Why didn't it only change the 1 member?