bug-bash
[Top][All Lists]
Advanced

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

inconsistent function of "-v" use w/arrays and hashes


From: Linda Walsh
Subject: inconsistent function of "-v" use w/arrays and hashes
Date: Sun, 14 Feb 2016 16:34:19 -0800
User-agent: Thunderbird

I seem to remember some discussion of this before --
not being able to use -v to check if a hash was defined,
but having it "work"(?) w/arrays?

I think, though, the fact that it operates inconsistently
makes it a bug.  I.e. if always work or fail, at least it
is behaving consistently, feels wrong.

The rule for whether it works or not is whether or not
the there is a value for '[0]' -- whether it is a hash or an
array.  I.e:

 hash hash_cfg=() dummy_hash
 array array_cfg=() dummy_array
# define exists func to make life easier...
exists () { see below...; } type exists
exists ()
{
   (($#)) || {
       printf '"Null" is not set (no param)';
       return 1
   };
   printf 'var "%s" is' "$1";
   [[ -v $1 ]] || printf " not";
   printf ' set\n'
}

# 1st check what bash "thinks":
 my -p hash_cfg array_cfg dummy hash dummy array
declare -A hash_cfg='()'
declare -a array_cfg='()'
-bash: declare: dummy_hash: not found
-bash: declare: dummy_array: not found
# bash thinks only the assigned-to vars "exist" - "normal"

# assign some values:
 array_cfg+=([1]=one)
 hash_cfg+=([one]=1)
 exists hash_cfg
var "hash_cfg" is not set
 exists array_cfg
var "array_cfg" is not set
# "-v" doesn't think either is set

# but bash shows "set" content in each:
 my -p hash_cfg array_cfg
declare -A hash_cfg='([one]="1" )'
declare -a array_cfg='([1]="one")'


# assign some more values
 hash_cfg+=([zero]=0)
 array_cfg+=([0]=zero)
 exists hash_cfg; exists array_cg
var "hash_cfg" is not set
var "array_cfg" is set
# Now "-v" thinks the array is set, but not the hash

So for hashes and arrays, "-v" only looks at the [0] member,
which is more than a bit wrong... it would be like some
"newbie" thinking they could list the contents of an
array just by using "echo $NAME" , which, for an array lists
the "first" element[sic], but **NOT** for a hash:

 echo "h=$hash_cfg, a=$array_cfg"
h=, a=zero

# So lets give the hash a k,v pair of 0,zero:
 hash_cfg+=([0]=zero)
 exists hash_cfg
var "hash_cfg" is set

# how does echo work now?
 echo "h=$hash_cfg, a=$array_cfg"
h=zero, a=zero
# note that printing w/bash shows:
 my -p hash_cfg array_cfg
declare -A hash_cfg='([one]="1" [zero]="0" [0]="zero" )'
declare -a array_cfg='([0]="zero" [1]="one")'

i.e. if there is a _key_ or index of 0, it is printed, when
no subscript is given, whereas the manpage says:

  Referencing an array variable without a subscript is
  equivalent  to referencing the array with a subscript of 0.

It probably should say this is true for hashes as well (since
it is -- ;-).

Additionally, the manpage says:

  An array variable is considered set if a subscript has
  been assigned  a value.  The null string is a valid value.

But this isn't exactly true either:

 array new_ar=([1]=one)
 exists new_ar
var "new_ar" is not set
 echo "new_ar=$new_ar"
new_ar=

Only the assignment to element zero qualifies it as
existing w/-v, and the same holds true for a hash.


# Note, "-v" knows that other members in the hash and array
# are set, if it is asked about them:
 exists hash_cfg[one]; exists array_cfg[1]
var "hash_cfg[one]" is set
var "array_cfg[1]" is set


----
At the very least, the manpage should probably be updated
to talk about hash's.

Ideally, "-v" would be fixed to look at #members
of an array or hash and if >0, use that as "defined"
else not.  Would that be doable & not too difficult?
I enclosed a sample alpha-level function to sorta show
what I mean.  It needs to be 'sourced' to work properly,
then call it as:

  exists VARNAME

I don't see any possible compat probs -- only from the fact
that as it is now, it's not really usable to give useful
information about arrays and hashes...whereas returning
set only if the data object has members in it would seem
to be the most the closest parallel definition -- as for
vars -- it is supposed to return whether or not they are
defined and have been assigned to.

If people really felt strongly about it, though, I wouldn't
be totally adverse if it return "true" if it had been
assigned an empty list as well (i.e. array a=(); hash=h(); --
has those will show up as existing w/dcl's "-p".

Ooops... just noticed that it doesn't correctly report
on existence of functions...sigh... well later on that one!

;-)







#!/bin/bash -u 

# "proof-of-concept", "alpha level quality"
# -LAWalsh, 2016

export 
PS4='>${BASH_SOURCE:+${BASH_SOURCE[0]/$HOME/\~}}#${LINENO}${FUNCNAME:+(${FUNCNAME[0]})}>
 '

shopt -s expand_aliases
alias my=declare sub=function
alias int='my -i' array='my -a'  map='my -A'

sub _typ () {
        my tp_fld="" jnk="" 
        read -r jnk tp_fld jnk< <(
                my -p $1 2>/dev/null || {
                        array fdef=()
                        readarray fdef< <(my -pf $1 2>/dev/null)
                        if [[ ${#fdef[@]} && ${fdef[0-1]} =~ ^declare ]]; then
                                printf "%s" ${fdef[0-1]}
                        else echo ""; fi; }
        )

        if [[ ${#tp_fld} == 0 ]]; then
                echo ""
                return 0
        fi
        
        array ops=( $(echo "$tp_fld"|sed -r 's/-//g; s/(.)/\1 /g') )
        my md="" o="" sep="" vt=''
        int dset=0
        for o in "${ops[@]}"; do
                ((${#o})) || continue
                if [[ ! $vt && ${types[$o]:-""} ]]; then 
                        vt=${types[$o]}
                fi
                if [[ ! ${mods[$o]:=""} && ${mods[$o]} != ${vt:-} ]]; then 
                        md+="$sep${mods[$o]}"; sep=" "
                fi
        done
        : ${vt:=var}
        printf "%s%s\n" "${md:+"$md "}" $vt
        
}

sub exists () {
        map mods=(['i']=int ['l']=lower ['u']=upper 
                                                ['x']=exported ['r']=readonly)

        map types=(['a']=array ['A']=hash ['i']=int )

        (($#)) || {
                        printf '"Null" is not set (no param)';
                        return 1
        }
        my name=$1
        my set=0
        my tp=$(_typ "$name")
        if [[ $tp && $tp == array || $tp == hash ]];then
                set=$(eval "echo \${#$name[@]}")
        else [[ -v $name ]] && set=1; fi
        printf '%s "%s" is' "$tp" "$name";
        ((set)) || printf " not";
        printf ' set\n'
}

shopt -s extdebug

[[ $0 =~ bash ]] && (($#)) && {
        exists "$1"
}


reply via email to

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