bug-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Dynamic variable failure & equiv-const strings compare unequal


From: Linda Walsh
Subject: Dynamic variable failure & equiv-const strings compare unequal
Date: Thu, 22 Oct 2015 03:01:06 -0700
User-agent: Thunderbird



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?









reply via email to

[Prev in Thread] Current Thread [Next in Thread]