1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2024-04-27 12:25:18 +02:00

52759: ${ ... } trims one trailing newline; "${ ... }" preserves that newline.

This commit is contained in:
Bart Schaefer 2024-03-17 14:28:28 -07:00
parent 45b0a838aa
commit 25182cc2e6
5 changed files with 61 additions and 17 deletions

View File

@ -1,5 +1,10 @@
2024-03-14 Bart Schaefer <schaefer@zsh.org>
* 52759: Doc/Zsh/expn.yo, Etc/FAQ.yo, Src/subst.c,
Test/D10nofork.ztst: change ${ ... } substitution to trim one
trailing newline; instead "${ ... }" (with quotes) preserves that
newline. All trailing newlines are still trimmed in emulations.
* unposted: Etc/BUGS: HIST_IGNORE_DUPS mishandles quoted whitespace.
* 52752: Src/params.c, Test/B02typeset.ztst: more typeset -p fixes

View File

@ -1950,6 +1950,9 @@ the braces by whitespace, like `tt(${ )...tt( })', is replaced by its
standard output. Like `tt(${|)...tt(})' and unlike
`tt($LPAR())...tt(RPAR())', the command executes in the current shell
context with function local behaviors and does not create a subshell.
Word splitting does not apply unless tt(SH_WORD_SPLIT) is set, but a
single trailing newline is stripped unless the substitution is enclosed
in double quotes.
Note that because the `tt(${|)...tt(})' and `tt(${ )...tt( })' forms
must be parsed at once as both string tokens and commands, all other

View File

@ -1091,20 +1091,23 @@ sect(Comparisons of forking and non-forking command substitution)
mytt(set -- pos1 pos2 etc). Nothing that happens within mytt($(command))
affects the caller.
mytt($(command)) removes trailing newlines from the output of mytt(command)
when substituting, whereas mytt(${ command }) and its variants do not.
The latter is consistent with mytt(${|...}) from mksh but differs from
bash and ksh, so in emulation modes, newlines are stripped from command
output (not from tt(REPLY) assignments).
When not enclosed in double quotes, the expansion of mytt($(command)) is
split on tt(IFS) into an array of words. In contrast, and unlike both
bash and ksh, unquoted non-forking substitutions behave like parameter
expansions with respect to the tt(SH_WORD_SPLIT) option.
When mytt(command) is myem(not) a builtin, mytt(${ command }) does fork, and
typically forks the same number of times as mytt($(command)), because in
the latter case zsh usually optimizes the final fork into an exec.
Both of the mytt(${|...}) formats retain any trailing newlines,
except as handled by the tt(SH_WORD_SPLIT) option, consistent with
mytt(${|...}) from mksh. mytt(${ command }) removes a single final
newline, but mytt("${ command }") retains it. This differs from
bash and ksh, so in emulation modes, newlines are stripped even from
quoted command output. In all cases, mytt($(command)) removes all
trailing newlines from the output of mytt(command).
When mytt(command) is myem(not) a builtin, mytt(${ command }) does
fork, and typically forks the same number of times as
mytt($(command)), because in the latter case zsh usually optimizes
the final fork into an exec.
Redirecting input from files has subtle differences:
itemization(

View File

@ -1900,6 +1900,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
/* The command string to be run by ${|...;} */
char *cmdarg = NULL;
size_t slen = 0;
int trim = (!EMULATION(EMULATE_ZSH)) ? 2 : !qt;
inbrace = 1;
s++;
@ -2005,10 +2006,13 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
int onoerrs = noerrs, rplylen;
noerrs = 2;
rplylen = zstuff(&cmdarg, rplytmp);
if (! EMULATION(EMULATE_ZSH)) {
if (trim) {
/* bash and ksh strip trailing newlines here */
while (rplylen > 0 && cmdarg[rplylen-1] == '\n')
while (rplylen > 0 && cmdarg[rplylen-1] == '\n') {
rplylen--;
if (trim == 1)
break;
}
cmdarg[rplylen] = 0;
}
noerrs = onoerrs;

View File

@ -86,9 +86,39 @@ F:setting option inside is too late for that substitution
?(eval):8: no matches found: f?*
purr ${| REPLY=$'trailing newlines remain\n\n' }
0:newline removal should not occur
0:newline removal should not occur, part 1
>trailing newlines remain
>
>
purr ${ echo $'one trailing newline\nremoved\n\n\n' }
0:newline removal in ${ ... }, zsh mode
>one trailing newline
>removed
>
>
>
() {
emulate -L ksh
purl ${ echo $'all trailing newlines\nremoved\n\n\n' }
purr "${ echo $'all trailing newlines\nremoved\n\n\n' }"
}
0:newline removal in ${ ... }, emulation mode, shwordsplit
>all
>trailing
>newlines
>removed
>all trailing newlines
>removed
purr "${ echo $'no trailing newlines\nremoved\n\n\n' }"
0:newline removal should not occur, part 2
>no trailing newlines
>removed
>
>
>
>
() {
@ -159,7 +189,7 @@ F:Why not use this error in the previous case as well?
1:unbalanced braces, part 4+
?(eval):1: closing brace expected
purr ${ purr STDOUT }
purr "${ purr STDOUT }"
0:capture stdout
>STDOUT
>
@ -322,7 +352,7 @@ F:Fiddly here to get EOF past the test syntax
0:here-string behavior
>in a here string
<<<${ purr $'stdout as a here string' }
<<<"${ purr $'stdout as a here string' }"
0:another capture stdout
>stdout as a here string
>
@ -331,7 +361,7 @@ F:Fiddly here to get EOF past the test syntax
wrap=${ purr "capture in environment assignment" } typeset -p wrap
0:assignment context
>typeset -g wrap='REPLY in environment assignment'
>typeset -g wrap=$'capture in environment assignment\n'
>typeset -g wrap='capture in environment assignment'
# Repeat return and exit tests with stdout capture
@ -410,7 +440,7 @@ F:must do this before evaluating the next test block
0:ignored braces, part 1
>buried}
purr ${ purr ${REPLY:-buried}}}
purr "${ purr ${REPLY:-buried}}}"
0:ignored braces, part 2
>buried
>}
@ -418,7 +448,6 @@ F:must do this before evaluating the next test block
purr ${ { echo nested ;} }
0:ignored braces, part 3
>nested
>
purr ${ { echo nested } } DONE
1:ignored braces, part 4