Date: Thu, 18 May 2023 22:14:28 -0400 From: "Dale R. Worley" <wor...@alum.mit.edu> Message-ID: <874jo9kqyj....@hobgoblin.ariadne.com>
| Chet Ramey <chet.ra...@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.ra...@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