bug-bash
[Top][All Lists]
Advanced

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

Indices of array variables are sometimes considered unset (or just displ


From: Great Big Dot
Subject: Indices of array variables are sometimes considered unset (or just display an error).
Date: Mon, 05 Nov 2018 16:42:16 -0500
User-agent: mu4e 1.0; emacs 26.1

uname output: Linux ArchBox0 4.18.16-arch1-1-ARCH #1 SMP PREEMPT Sat Oct 20 
22:06:45 UTC 2018 x86_64 GNU/Linux
Machine Type: x86_64-unknown-linux-gnu
Bash Version: 4.4
Patch Level: 23
Release Status: release
--text follows this line--
Description:
        The parameter expansion "${!var[@]}" expands to the indices of an array
    (whether linear or associative). The expansion "${var-string}"
    returns "${var}" iff var is set and 'string' otherwise. These two
    features do not play well together:

        $ declare -a -- array=([0]=hello [1]=world)
        $ printf -- '%s\n\n' "${!array[@]-Warning: unset}"
        bash: hello world: bad substitution

        $ declare -a -- array=([0]='helloworld')
        $ printf -- '%s\n\n' "${!array[@]-Warning: unset}"
        Warning: unset

        $ declare -a -- array=([0]='hello world')
        $ printf -- '%s\n\n' "${!array[@]-Warning: unset}"
        bash: hello world: bad substitution

        $ declare -a -- array=()
        $ printf -- '%s\n\n' "${!array[@]-Warning: unset}"
        Warning: unset

    As you can see, accessing the index list of multiple-element arrays
    fails when you append the unset expansion. With single-element
    arrays, it fails iff the element in question contains any special
    characters or whitespace, and thinks the array is unset otherwise.
    (Further testing shows that a value of the empty string also throws
    an error.) Finally, empty arrays are also considered unset. (This is
    the one thing that is consistent with the rest of bash, since empty
    arrays themselves are also considered unset by this expansion; that
    is, "${array[@]-unset}" yields 'unset' when array isn't set.)

    This pattern of behavior is apparently unaffected by changes to IFS,
    using a normal variable as a one-element array, using an unset
    variable as a zero-element array, or using an associative instead of
    linear array. That last one has an interesting wrinkle, however:

       $ declare -A -- assoc=(['k e y']='element')
       $ printf -- '%s\n\n' "${!assoc[@]-Warning: unset}"
       Warning: unset

       $ declare -A -- assoc=(['key']='e l e m e n t')
       $ printf -- '%s\n\n' "${!assoc[@]-Warning: unset}"
       bash: e l e m e n t: bad substitution

    Strangely, whether a single-element array errors (as opposed to
    giving the wrong result) is only dependent on the the characters in
    the *element*, not the *key*---despite the fact that only the key's
    value is being requested!


Repeat-By:
        $ declare -a arr_2_=(zero one);  printf '%s\n' "${!arr_2_[@]-unset}"
    bash: zero one: bad substitution
        $ declare -a arr_1a=('z e r o'); printf '%s\n' "${!arr_1a[@]-unset}"
    bash: z e r o: bad substitution
    $ declare -a arr_1b=('zero');    printf '%s\n; "${!arr_1b[@]-unset}"
    unset

Fix:
    To avoid this problem, you just need to spend another line or two
    writing out the relevant conditional explicitly; for example:

        # <command> ... "${!array[@]-<default>}"
        if [ -v 'array[@]' ]; then
            <command> ... "${!array[@]}" ...
        else
            <command> ... <default> ...
        fi

    Note that `test -v 'array[@]'` has the same "feature" that
    "${array[@]-default}" does: it treats empty arrays as unset.



reply via email to

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