mirror of
git://git.code.sf.net/p/zsh/code
synced 2025-05-18 09:41:09 +02:00
52759: ${ ... } trims one trailing newline; "${ ... }" preserves that newline.
This commit is contained in:
parent
45b0a838aa
commit
25182cc2e6
5 changed files with 61 additions and 17 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
21
Etc/FAQ.yo
21
Etc/FAQ.yo
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue