help-bash
[Top][All Lists]
Advanced

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

Re: [Help-bash] Assigning builtins behavior


From: Andy Chu
Subject: Re: [Help-bash] Assigning builtins behavior
Date: Sun, 7 Jan 2018 18:17:36 -0800

This is a good observation -- I didn't see any mention of this POSIX [1].
I looked in the sections on field splitting, the shell grammar, and the
list of builtins, which includes readonly and export.  And it also has
guidelines for utility syntax conventions [2].

The shell grammar doesn't make any mention of assignments.  Assignments are
presumably just commands.

However in bash you can search for parse_compound_assignment() in parse.y.
The parser has knowledge of assignments -- it's not completely deferred
until runtime.

It sets a flag in the parser state on line 6234:

  parser_state |= PST_COMPASSIGN;

Then later in read_token_word(), it checks this flag and then sets NOSPLIT
and NOGLOB flags:

      /* Don't perform word splitting on assignment statements. */
      if (assignment_acceptable (last_read_token) || (parser_state &
PST_COMPASSIGN) != 0)
        {
          the_word->flags |= W_NOSPLIT;
          if (parser_state & PST_COMPASSIGN)
            the_word->flags |= W_NOGLOB;        /* XXX - W_NOBRACE? */
        }
    }


I don't think there's any reason for this other than "shells historically
did it this way", and it's impossible for POSIX to be 100% complete.  My
experience in writing a shell is that most shells are very POSIX
conformant.  But that's because POSIX doesn't address many cases.  I'd
estimate that the feature set of bash is at least 2x bigger than that of
POSIX, maybe 5x bigger.

FWIW, as I was implementing my shell, I naturally implemented this rule,
because I intuitively know it from writing shell.  I have an
EvalWordSequence() function which does splitting and globbing, and an
EvalWordToString(), which doesn't.

There are other places where words are not split, for example, shells vary
widely on the behavior of this snippet:

file='a b'
echo foo > $file  # unquoted redirect filename

Bash gives an error, but some shells will write a file called 'a b'.  You
can also try that with an array like "address@hidden".  IIRC, zsh writes 
MULTIPLE
files!

Another place where words are not split is the case pattern, because it
doesn't make much sense:

x='a b'
case $x in $x) echo yes;; esac
yes


Still I agree that if it's indeed not mentioned, it's a pretty big
oversight!

Andy



[1] http://pubs.opengroup.org/onlinepubs/9699919799/utilities/contents.html

[2]
http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html#tag_12_02




http://pubs.opengroup.org/onlinepubs/9699919799/utilities/contents.html

On Sun, Jan 7, 2018 at 1:10 PM, Quentin L'Hours <address@hidden>
wrote:

> Hi,
>
> I've always thought builtins could not have a custom parsing, however it
> seems that export/readonly/declare/local (all the builtins creating an
> assignment) are able to disable field splitting when an assignment takes
> place, why is that? I know a simple assignment disables field splitting,
> but after all that's because the grammar states it should, but doesn't
> POSIX requires all simple commands to have the same splitting behavior?
>
> bash$ foo=$(echo 1 bar=2)
> bash$ echo "$foo"
> 1 b=2
>
> This is logic, simple assignment doesn't triggers field splitting, that's
> the rule.
>
> bash$ set a=$foo
> bash$ echo "$1"
> a=1
>
> This is logic too, field splitting happens as expected
>
> bash$ readonly a=$foo
> bash$ echo "$a"
> 1 b=2
>
> Why? I know simple assignment doesn't split but I would have thought this
> should still follow the simple commands arguments expansion and thus should
> do 2 assignments. I mean builtins shouldn't be able to have their custom
> parsing, if they do then they should be classified as keywords (like [[
> ]]). What should be the POSIX way of doing things? Is this a bash extension?
>
> As a side note ksh has the same behavior, but dash follows what I thought
> would be the "normal" behavior:
>
> dash$ foo=$(echo 1 b=2)
> dash$ readonly a=$foo
> dash$ echo "$a"
> 1
> dash$ echo "$b"
> 2
>
> Thanks,
>
> --
> Quentin
>
>


reply via email to

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