bug-bash
[Top][All Lists]
Advanced

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

Re: Funny behaviour of associative arrays


From: Robert Elz
Subject: Re: Funny behaviour of associative arrays
Date: Tue, 27 Jun 2023 18:30:07 +0700

    Date:        Tue, 27 Jun 2023 07:29:42 +0200
    From:        n952162 <n952162@web.de>
    Message-ID:  <e3850500-84f6-c963-18ff-1a7a660637ef@web.de>

I suspect this message really should have gone to the bash-help list.
Never mind:

  | If so, why?

I think this is related to the message I sent to the list early
this morning (my time) -- bash is deciding what it has by looking
at the characters in the line.

In:
        l1=([a]=b [c]=d)

it sees an assignment statement, with keys and values.

In:
         l1=($(echo [a]=b [c]=d))

it doesn't, all that's there is an array assignment using the results
of a command substitution for the values.  At this point, there's no
clue that what that list will contain will look like keys and values,
so it is treated differently.

  | And how can I assign a list of members to an associative array?

The first example above does that, but I am guessing you mean dynamically,
rather than written into the script.

        eval "l1=($(echo [a]=b [c]=d))"

will do that, as when the assignment is being parsed (by eval) the
command substitution has already run (part of expanding the args passed
to eval) and the actual assignment being run looks just like the first
example - except it was dynamically generated by the command substitition.

Or that is, it probably will - "echo" is not any kind of declaration
utility, nor is it an assignment statement, so its args are parsed like
args to any other command.   That includes pathname expansion.  If there
happen to be files named a=b or c=d (or both) in the current directory,
then those patterns (which is what they are when args to echo, or almost
any other command) will be replaced by the expansions, and what will be
executed by eval (if both those files exist) is

        eval "l1=(a=b c=d)"

which is not what you intended.   If those files (one or both) don't
exist (and the relevant bash option to alter the default behaviour of
a failure to match is not enabled) then the pattern that didn't match
is left unchanged, so it seems to work.   But only seems to, it is
working by chance.

You need to quote things that you don't want to be expanded if they
appear in a context where they would be.  So the command should really
be

        eval "l1=($(echo '[a]=b' '[c]=d'))"

Of course, this is a dummy test case, obviously, the real code which
would be in that command substitution is, I assume, rather more complex.
How to quote that, if quoting is needed at all, depends upon what is
being executed.   You are also relying upon field splitting happening,
so the output from the command substitution isn't treated as a single
string, but as two strings (I don't know bash array assignment syntax
well enough to know if that matters, it might not).   If a single string
there is OK, and it seems to be, then

        eval "l1=("$(echo '[a]=b' '[c]=d')")"

is even better, as that avoids issues if IFS doesn't contain what you
expect it to contain at the time.

kre

ps: the single quotes only need to be around the '[' (and a \ before it
could be used as well) but the form given above looks nicer, IMO.  All
that is really needed is to inhibit pathname expansion, by not having
any unquoted glob chars there (* ? or [) where a '[' also needs a terminating
unquoted ']', with something else between them, to count as a pattern char).





reply via email to

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