bug-bash
[Top][All Lists]
Advanced

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

Re: nofork command substitution


From: Robert Elz
Subject: Re: nofork command substitution
Date: Fri, 19 May 2023 17:11:42 +0700

    Date:        Thu, 18 May 2023 22:14:28 -0400
    From:        "Dale R. Worley" <worley@alum.mit.edu>
    Message-ID:  <874jo9kqyj.fsf@hobgoblin.ariadne.com>

  | Chet Ramey <chet.ramey@case.edu> writes:
  | > Bash allows the close brace to be joined to the remaining
  | > characters in the word without being followed by a shell metacharacter
  | > as a reserved word would usually require.
  |
  | I had to read this a couple of times to figure out what it means.  In
  | particular "the word" isn't well-bound here.  Perhaps better is

I'm not sure why this is worth mentioning at all, any more than that
more text, that is part of the same word, can follow a ${var} expansion.
These things (variable expansions, command substitutions, arithmetic
expansion, tilde exbansion, and in bash and some other shells, brace
expansion) are all word expansions.   All of them occur anywhere in a
shell "word", and I don't see this proposed new form as being any different.

The only oddity is that inside the nofork cmd sub, the closing '}' is being
treated as a reserved word (as it is when used for grouping lists), but from
the outside, it is just a part of the word that contains it.

  | My guess is that the intention is for COMMAND to be "read" with no names:
  |     ${| read; } 

I'd have thought a more likely example would be

        ${| printf -v REPLY 'whatever format' arg... ; }

to get specifically formatted text into a shell word, without needing
a fork, which the alternate
        $( printf 'whatever format' arg.... )
would require.

Use of REPLY in particular as the variable to contain the result seems
like just because it is used that way in read (where it is never really
needed, that's the ultimate of useless frills) and select, and so is a var
name already more or less reserved for the shell's internal use (like OPTIND,
OPTARG, IFS, ...) and was just used for the purpose.   Personally I'd
prefer if this kind of command substitution 

[On trailing \n]
  |  Is that what you intend the code to do/what the code does?

In the example you gave, it would be.   To me, the sole use of the ${| form
seems to be that it doesn't delete the trailing \n if any (Chet didn't actually
say that the other forms do delete trailing newlines, but his example:

chet.ramey@case.edu said:
  |     For example, this construct expands to '12345', and leaves the shell
  | variable 'X' unchanged in the current execution environment:
  |       ${ local X=12345 ; echo $X; } 

implies that they do (since echo adds a \n, something must be removing it to
get the '12345' result of the expansion).

I'd have thought it better to simply make ${| be the same as the variant
using the space (there's no hint in the brief spec as to what difference
it would make using tab or newline, if any, though they were expressly
mentioned as possible) just with newline supression removed.   It would
be possible to do the same with $(| make that suppress trailing newline
removal as well).    That is, keep ${| using stdout for its result, rather
than yet another meaingless use of REPLY.

And I fail to see any need at all for the ${( form - I see no difference
in that one from $( ) except more, and more difficult, syntax, hence
completely pointless, unless there was something missing I failed to grasp.
I suppose the intent was still to retain the "nameless function" semantic,
in the sub-shell, so return would work (not needed there, exit works just
as well) and so local vars can exist - but that's meaningless, in a subshell
all variables are essentially "local" from the point of view of the enclosing
shell, writing "local" for them just makes the shell do more work, for no
benefit whatever.   I'd just eliminate the ${( form, it seems to be useless.

Personally, rather than the "nameless function" semantic, I'd prefer the
exact opposite - a word expansion form which runs a named function (just like
any other function, without forking) and substitutes its stdout.   That's
something I've been considering adding to the shell I maintain for ages,
but I haven't really come across the ideal syntax (and I'm not sure dollar
brace space is it either).   The difference between that, and the nameless
function variant suggested, seems largely to be in the treatment of the
positional parameters, where a function can't see the outside $1 $2 ...
(nor change them).  For seeing them, just calling the function with "$@"
as the parameter, perhaps "$#" "$@" other args, should work OK I thing (but
I haven't ever really had the need, so haven't tried it - simply passing "$@"
as the args to a function is simple - done that lots of times).

In a normal function call not being able to alter the (outside) positional
params can be annoying sometimes (I'd like to write "rotate" as a function,
rather than being required to either make it a variant of shift, or a new
built-in command) but a more general solution for that, and the need to
sometimes be able to access a global var with a name passed as an arg, which
by sheer bad luck (or malicious intent) happens to be the same name as a local
var in a function.   There are times it is necessary to explicitly refer to
the calling environment, so some kind of "upvar" (from other languages) seems
like it would be something good to add.   I'd considered ${^var} where "var"
in this case isn't required to be a name, but can be the result of an expansion
like ${^$1} to get the external variable whose name is in $1 ($1 is the local
positional param in the function) and then ${^@} would work to access the
external positional params.   All that's needed beyond that is an option to
shift (say -U .. upper case for a reason that will become clear in a second)
to have it operate on the "outside" positional parameters, and similarly for
"set" to allow it to set the "outside" (upper) ones - hence -U as the shell
has no 'U' option to set, but does have a 'u' option, so set can't steal that
one for this purpose.

But to go back to the first few words of the previous para - a command
substitution is never going to be a "normal" function call, and I cannot
see any benefit in allowing that form (and only that one - if it were simply
a possible side effect of a more general mechanism, that would be different)
to alter the external positional parameter list.

Trying to explain to someone what

        "$*${ set -- a b c;}$*"

produces, and why that's a good idea, would be kind of difficult I think.

That's different from

        "${ cd /tmp; echo foo; }"

in that there's no special magic being invented to allow cd in particular
to work there - what happens is just a side effect of the no fork semantic.

if the "${ " thing weren't being explained (and presumably implemented) as
a function, then the "set" example above would just be another example.  But
as it is to be more or less a function, complete with local vars, it should
(whether implemented as a nameless function, or as a call of a named one)
be a real function call, with no variant semantics (and so, a local set of
positional parameters).

Chet: since field splitting and pathname expansion come after word expansions
you're also going to need to explain what happens with things like

        "${ IFS= ; echo foo; }${ unset IFS; echo bar;}${ IFS=' '; echo a b c;}"

and
        "${ set -f; echo '*';}${ set +f; echo '???'; }"

and lots more like it.

kre




reply via email to

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