[Top][All Lists]

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

Re: Indices of array variables are sometimes considered unset (or just d

From: Chet Ramey
Subject: Re: Indices of array variables are sometimes considered unset (or just display an error).
Date: Tue, 6 Nov 2018 10:37:29 -0500
User-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:52.0) Gecko/20100101 Thunderbird/52.9.1

On 11/5/18 8:44 PM, Great Big Dot wrote:

> What's actually happening here is that the *indirection* expansion
> "${!foo}", and not the *indices* expansion "address@hidden", is what is being
> preformed on something like "address@hidden". Both expansions, while
> unrelated, happen to use the same syntax, with the exception that
> indirections apply to normal variables and index expansions apply to array
> variables. For some reason, adding on the "${foo-default}" expansion causes
> the former to be used instead of the latter. 

The `some reason' is that the current behavior is documented and has been
the way bash has worked since I added indirect expansion (bash-2.0) and the
array keys expansion (bash-3.0).

> This can be seen here:

I'm going to use bash-4.4 for my explanation.

>     $ array=(foo)
>     $ printf -- '%s\n' "address@hidden"
>     unset

Of course. There is no variable "foo"; the indirect expansion results in a
null string.

>     $ foo='hello world'
>     $ printf -- '%s\n' "address@hidden"
>     hello world

(I don't get that result with any version of bash. I went back to bash-3.0
before I quit trying.)

This is a case where bash is trying to be helpful, maybe more so than is
desired. `foo' isn't an array variable, but the address@hidden is first 
as a candidate for indirect expansion, then checked to see whether or not
it is the entire expansion between ${ and }. Since it's not, it's not a
candidate for array keys expansion (this is as documented).

Since the array keys expansion isn't valid, the attempt to perform variable
indirection holds. This is where the helpful part comes in. Bash variables
can be referenced as arrays, even if they are not, using `0', `@', or `*'
as subscripts. That means that address@hidden gets expanded into "hello world",
which the shell tries to use as a variable name, resulting in:

$ ../bash-4.4-patched/bash ./x16
./x16: line 2: hello world: bad substitution

The error message in bash-5.0 is a little better:

$ ../bash-5.0-beta/bash ./x16
./x16: line 2: hello world: invalid variable name

>> This pattern of behavior is apparently unaffected by changes to IFS[...]
> Upon further examination, and in light of the above realization, this
> actually isn't true. In particular, iff the first character of IFS is
> alphanumeric or an underscore (or if IFS is the empty string), and if you
> use the "${array[*]}" form instead, then the expansion doesn't throw an
> error when the array contains more than one element.

Sure, since a double-quoted expansion using `*' separates words using the
first character of IFS.

>     $ foo_bar='Beto2018'
>     $ printf -- '%s\n' "${!array[*]-Warning: unset}"
>     Beto2018

Nice. Let's hope he pulls it off today.

> Is there a good reason for treating "address@hidden" and "${!array[*]-}"
> like indirections instead of index expansions (or just throwing an error)?

When I added array variables and the array keys expansion, I used the ksh93
syntax (${!var[sub]}) and tried to avoid conflict with the existing
indirect expansion as much as possible (that was back when we still thought
there was a chance that POSIX would standardize arrays and it was useful to
have consistent implementations). The ksh93 expansion syntax made it
invalid to use the array keys expansion as part of the ${param:-word}
expansion, so that's how I made it work. Since it wasn't a candidate for
that family of expansions, the existing variable indirection syntax

``The lyf so short, the craft so long to lerne.'' - Chaucer
                 ``Ars longa, vita brevis'' - Hippocrates
Chet Ramey, UTech, CWRU    address@hidden    http://tiswww.cwru.edu/~chet/

reply via email to

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