[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Help-bash] Destroying arbitrary subset in an associative array?
From: |
Dan Douglas |
Subject: |
Re: [Help-bash] Destroying arbitrary subset in an associative array? |
Date: |
Sun, 18 Sep 2016 22:01:08 -0500 |
On Sun, Sep 18, 2016 at 2:10 PM, Mingye Wang (Arthur2e5)
<address@hidden> wrote:
> Hello,
>
> I have a set of functions in a script that wraps around operations in a
> certain associative array, $dict. When I try to unset a subset with quotes
> or backslashes, however, my "del" function always fail with "not a valid
> identifier". I am using bash 4.3.42.
>
> The problem boils down to this snippet:
>
> declare -A dict
> dict['"']=1
> dict['`']=2
> dict["'"]=3
> dict['\']=4
> declare -p dict
>
> del(){ unset -v dict["$1"]; }
> for k in "address@hidden"; do del "$k"; done
>
> I have also tried `unset dict["'"]` manually, which gives me "bad array
> subscript" instead.
Bash evaluates expansions in array indices dynamically as part of the
process of resolving its value. Often that means array indices must be
regarded as unexpanded strings that are dynamically evaluated later.
That solves this very problem (and many other less obvious ones) in
about the simplest possible way, at the cost of requiring everyone
know when to add extra quoting.
~ $ bash /dev/fd/9 9<<\EOF
typeset -A a=( [\\]= [\"]= [\)]= ) b
for x in "address@hidden"; do b[$x]=; done
b+=([\`]= [\]]=)
typeset -p b
for x in "address@hidden"; do
unset -v 'b[$x]'
done
typeset -p b
EOF
declare -A b='(["\\"]="" ["]"]="" ["\`"]="" ["\""]="" [")"]="" )'
declare -A b='(["]"]="" ["\`"]="" )
Unfortunately, this isn't portable to ksh arrays because it wants to
know the type of the variable and its subscript and resolves it
immediately without evaluating expansions. On top of that, it's buggy
and can't parse assignment arguments and their expansions correctly.
One theoretically portable workaround is:
~ $ ksh /dev/fd/9 9<<\EOF
typeset -A a=( [\\]= [\"]= [\)]= ) b
for x in "address@hidden"; do b[$x]=; done
b+=([\`]= [\]]=)
typeset -p b
eval unset -v "$(printf -- 'b\[%q\] ' "address@hidden")"
typeset -p b
EOF
typeset -A b=(['"']='' [')']='' ['\']='' [']']='' ['`']='')
typeset -A b=(['\']='' ['`']='')
That doesn't work in bash. No idea why. I guess it's doing a
quote-removal step after the expansion even if there was nothing to
escape but a bare escape character.
~ $ bash -x /dev/fd/9 9<<\EOF
typeset -A a=( [\\]= [\"]= [\)]= ) b
for x in "address@hidden"; do b[$x]=; done
b+=([\`]= [\]]=)
typeset -p b
eval unset -v "$(printf -- 'b\[%q\] ' "address@hidden")"
typeset -p b
EOF
+ a=([\\]= [\"]= [\)]=)
+ typeset -A a b
+ for x in '"address@hidden"'
+ b[$x]=
+ for x in '"address@hidden"'
+ b[$x]=
+ for x in '"address@hidden"'
+ b[$x]=
+ b+=([\`]= [\]]=)
+ typeset -p b
declare -A b='(["\\"]="" ["]"]="" ["\`"]="" ["\""]="" [")"]="" )'
++ printf -- 'b\[%q\] ' '\' '"' ')'
+ eval unset -v 'b\[\\\] b\[\"\] b\[\)\] '
++ unset -v 'b[\]' 'b["]' 'b[)]'
/dev/fd/9: line 5: unset: `b[\]': not a valid identifier
/dev/fd/9: line 5: unset: `b["]': not a valid identifier
+ typeset -p b
declare -A b='(["\\"]="" ["]"]="" ["\`"]="" ["\""]="" )'