Dynamic variable failure & equiv-const strings compare unequal

2015-10-22 Thread Linda Walsh



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?









Re: Dynamic variable failure & equiv-const strings compare unequal

2015-10-22 Thread Linda Walsh



Oleg Popov wrote:

On Thu, Oct 22, 2015 at 03:01:06AM -0700, Linda Walsh wrote:

[cut]
I.e. test output was:
Case 2 got/Expected:
"222"
"1\ 222\ .3\ .4"
[cut]


You didn't initialize the array. By the time you do "parts[1]=222" it's 
still empty. And in your previous message you tried to initialize it in 
a subshell. Variables don't retain their values after returning from 
subshells.  


	I was testing if dynamic scoping included subshells, I 
didn't think so, but that doesn't mean I don't test it.  I removed

it though, as it confused the example.

ip and 'parts' are both initialized in global.

testor calls (tst0, tst1, tst2 & tst3).

tst0 & tst1 both call "assignparts" which uses the global
value of $ip to set the global value of parts.  I.e. since
neither "ip" nor 'parts' are declared inside of any of the 
functions, they should use the top-level global values, no?


tst2, using the last global value set in tst1, only tries to
change 1 value in 'parts'... i.e. why would 'ip' reference the
global value of 'ip', but not parts?

ip and parts are declared at the same scope (global), so why
wouldn't the global 'parts' be initialized as well?



Bash uses unquoted characters on the right side of == and != as wildcard 
patterns. For example, [ipaddr] in $exp means "any of i, p, a, d, r".  
You should quote the right operand.

---
	Ahh... or if I quote both sides 
using 'printf "%q"' first, that should do the same, no?


So, ok, I get that one -- but the first looks sketchy,
as ip and parts are both, only defined at the global level, 
thus my expectation that just like it used the global value of

'ip' for tst0 & tsts1 -- it should have stored the split version
of it in the global value of parts...  I really don't get why
it would use the global value as a dynamic in 1 case but not
the other...?









Re: Dynamic variable failure & equiv-const strings compare unequal

2015-10-22 Thread Chet Ramey
On 10/22/15 8:13 AM, Linda Walsh wrote:
> 
> 
> Oleg Popov wrote:
>> On Thu, Oct 22, 2015 at 03:01:06AM -0700, Linda Walsh wrote:
>>> [cut]
>>> I.e. test output was:
>>> Case 2 got/Expected:
>>> "222"
>>> "1\ 222\ .3\ .4"
>>> [cut]
>>
>> You didn't initialize the array. By the time you do "parts[1]=222" it's
>> still empty. And in your previous message you tried to initialize it in a
>> subshell. Variables don't retain their values after returning from
>> subshells.  
> 
> I was testing if dynamic scoping included subshells, I didn't think so,
> but that doesn't mean I don't test it.  I removed
> it though, as it confused the example.

You don't show what you `removed', so I am looking at the original script
you posted.

> ip and 'parts' are both initialized in global.

Yes.  parts is assigned the empty array at the global scope.

> testor calls (tst0, tst1, tst2 & tst3).

In subshells started to run command substitution.  Those subshells
modify `parts', but the changed value is not reflected in the parent
shell.

> 
> tst0 & tst1 both call "assignparts" which uses the global
> value of $ip to set the global value of parts.  I.e. since
> neither "ip" nor 'parts' are declared inside of any of the functions, they
> should use the top-level global values, no?

You. Run. Your. Tests. In. Subshells.

> tst2, using the last global value set in tst1, only tries to
> change 1 value in 'parts'... i.e. why would 'ip' reference the
> global value of 'ip', but not parts?

You modify parts in a subshell.  The global value of parts is initialized
to and remains an empty array.

-- 
``The lyf so short, the craft so long to lerne.'' - Chaucer
 ``Ars longa, vita brevis'' - Hippocrates
Chet Ramey, ITS, CWRUc...@case.eduhttp://cnswww.cns.cwru.edu/~chet/



Re: Dynamic variable failure & equiv-const strings compare unequal

2015-10-22 Thread Linda Walsh



Oleg Popov wrote:
$(...) is a subshell. Variables cannot be passed back from a subshell, 
no matter how and where they are declared.

---
Um... oh.. in testor, still calling that way.  I
missed that.

This is even more annoying for passing back results
than I thought.  Grrr.




Re: Dynamic variable failure & equiv-const strings compare unequal

2015-10-22 Thread Oleg Popov
On Thu, Oct 22, 2015 at 03:01:06AM -0700, Linda Walsh wrote:
> [cut]
> I.e. test output was:
> Case 2 got/Expected:
> "222"
> "1\ 222\ .3\ .4"
> [cut]

You didn't initialize the array. By the time you do "parts[1]=222" it's 
still empty. And in your previous message you tried to initialize it in 
a subshell. Variables don't retain their values after returning from 
subshells.

> [cut]
> 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.
> [cut]

> [cut]
>if [[ $out != $exp ]]; then
> [cut]

Bash uses unquoted characters on the right side of == and != as wildcard 
patterns. For example, [ipaddr] in $exp means "any of i, p, a, d, r".  
You should quote the right operand.


> #!/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?
> 
> 
> 
> 
> 
> 
> 



Re: Dynamic variable failure & equiv-const strings compare unequal

2015-10-22 Thread Oleg Popov
On Thu, Oct 22, 2015 at 05:13:45AM -0700, Linda Walsh wrote:
> Oleg Popov wrote:
> > On Thu, Oct 22, 2015 at 03:01:06AM -0700, Linda Walsh wrote:
> >> [cut]
> >> I.e. test output was:
> >> Case 2 got/Expected:
> >> "222"
> >> "1\ 222\ .3\ .4"
> >> [cut]
> > 
> > You didn't initialize the array. By the time you do "parts[1]=222" it's 
> > still empty. And in your previous message you tried to initialize it in 
> > a subshell. Variables don't retain their values after returning from 
> > subshells.  
> 
>   I was testing if dynamic scoping included subshells, I 
> didn't think so, but that doesn't mean I don't test it.  I removed
> it though, as it confused the example.
> 
> ip and 'parts' are both initialized in global.
> 
> testor calls (tst0, tst1, tst2 & tst3).
> 
> tst0 & tst1 both call "assignparts" which uses the global
> value of $ip to set the global value of parts.  I.e. since
> neither "ip" nor 'parts' are declared inside of any of the 
> functions, they should use the top-level global values, no?
> 
> tst2, using the last global value set in tst1, only tries to
> change 1 value in 'parts'... i.e. why would 'ip' reference the
> global value of 'ip', but not parts?
> 
> ip and parts are declared at the same scope (global), so why
> wouldn't the global 'parts' be initialized as well?

$(...) is a subshell. Variables cannot be passed back from a subshell, 
no matter how and where they are declared.

> > Bash uses unquoted characters on the right side of == and != as 
> > wildcard patterns. For example, [ipaddr] in $exp means "any of i, p, 
> > a, d, r".  You should quote the right operand.
> ---
>   Ahh... or if I quote both sides 
> using 'printf "%q"' first, that should do the same, no?

No. Just use double quotes:
[[ $var1 == "$var2" ]]

>   So, ok, I get that one -- but the first looks sketchy,
> as ip and parts are both, only defined at the global level, 
> thus my expectation that just like it used the global value of
> 'ip' for tst0 & tsts1 -- it should have stored the split version
> of it in the global value of parts...  I really don't get why
> it would use the global value as a dynamic in 1 case but not
> the other...?