tech-userlevel archive

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

Re: bin/47597: local and $() don't play well with each other



    Date:        Wed, 27 Feb 2013 13:09:26 +0100
    From:        tlaronde%polynum.com@localhost
    Message-ID:  <20130227120926.GA251%polynum.com@localhost>


  | The problem is that the specification allows both interpretations,
  | because there is no definition of "utility"

There is, see 4.20  (it is basically something implemented outside the
shell, it can be an executable, or a script, but not a built-in or function)

  | It seems at least that:
  | 
  | export VAR=value
  | 
  | sets and exports. There is no obligation to interpret this as
  | "assignement made at the beginning of the instruction" like in a
  | "custom" environment.

Not only is there no obligation to do that, it would be wrong to do so.

After the command name, there are just parameters, that export happens
to treat its parameters as variables that can be assigned to is just
because of the definition of that particular command - for the
general syntax rules, we cannot depend upon how some particular command
happens to interpret its arguments.   Consider instead

        dd if=file

In that, no-one (who knows dd anyway) believes that is assigning the
name (or string) "file" to the variable "if"...

  | Furthermore, this means that the behavior of an
  | external utility and the behavior of the same made as a function will be
  | different.

Yes, it does, and this is a problem, yet it seems to be intentional in 2.9.5
(and from people who know from the opengroup list/newsgroup seems to have been
intended because of how existing shells were implemented).

On the other hand, this contradicts 4.20 which requires that (aside from
using tricks) there should be no way to detect whether a command is
implemented as a function, built-in, or utility (note the special built-ins
are not included in this relationship).

[Tricks mean things like measuring performance, and inferring whether a
command is built-in or a utility from how fast it runs, or knowing the
command search rules and inserting a script with the name of the command
being tested in a directory at the start of $PATH - if the script is executed, 
then the command is a utility, if it is not, then it would be a function
of built-in.]

  | One can prevent namespace modifications within the
  | implementation of a function,

One can, by not making any (or by doing everything in a sub-shell) but
those are the only portable ways.

  | but one can not prevent a user to call an
  | external utility and a function the same way, having differing
  | behaviors. POSIX is about portability...

It is, but it is also a standard, and if done properly, standards document
the state of the world, they do not attempt to legislate what that state
should be in an ideal world.   Portability is achieved because everyone
knows what they can rely upon, and what they cannot (and implementors of shells
know what they need to implement in a specific way to meet user expectations,
and what they have freedom to experiment with).   If the existing shells
(at the time) all implemented
        var=value func_call
as leaving $var set to value after func_call completes, then that is
what the standard has to say happens - regardless of how stupid it is.

  | The lesson (for me) is that the spec is indeed ambiguous

It is actually not (on this point) everything I've read since I first asked
this question has convinced me of that (there are some open questions, but
not on this particular point - for example, while it is clear that the
value assigned to var is intended to perisist after the func call ends,
it isn't clear if it is intended to be exported or not, that is, if we do:

        unset var
        func() { :; }
        var=value func
        echo $var                       # produces "value"
        env | grep ^var                 # ? whether var is in the environment 
is unclear

On NetBSD, ksh does echo "value" (but does not export it), sh echoes nothing
(well, a newline) (and of course, does not export an unset variable), and
bash has a foot in each camp, in its default mode, it acts just line NetBSD's
sh, but if you turn on posix mode (set -o posix) then it echos value
(like ksh) but also exports it.

  | and that there is no de facto standard (all shells behaving differently).

Here I agree.

  | So there is no urge to change NetBSD sh to match an interpretation
  | that is not guaranteed to be "the" specification.

I'm kind of hoping for something more than "no urge" --- I'd prefer an
explicit statement that NetBSD is going to ignore the standard in this
area.

That (incidentally) is a good thing - it is how the standard gets fixed.
If everyone just implements what it says, then whatever it says remains,
however insane.  But when implementations ignore it, and implement something
different, then either the standard gets changed to specify the something
different, if there is one such thing, or gets changed to indicate that
the behaviour is undefined (so at least users know not to assume that
things will always work the way that the shell they normally use works).

  | Furthermore, I fail to see why a user will write:
  | 
  | var=value utility
  | 
  | expecting var=value to remain, while simply making this an instruction
  | (by a newline or a semi-colon) will portably do the very same thing.

Well, if it is a utility, the value would not remain, it remains only for
functions, and special built-ins, neither of which are utilities.

But ignoring that quibble, I agree, it makes no sense at all, and everyone
seems to agree with that - now it just takes the right people to present
this argument (along with the behaviour of existing shells) the next time
the standard is being revised.
 
kre



Home | Main Index | Thread Index | Old Index