mirror of
git://git.code.sf.net/p/zsh/code
synced 2024-05-10 17:46:19 +02:00
52759: ${ ... } trims one trailing newline; "${ ... }" preserves that newline.
This commit is contained in:
parent
45b0a838aa
commit
25182cc2e6
|
@ -1,5 +1,10 @@
|
||||||
2024-03-14 Bart Schaefer <schaefer@zsh.org>
|
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.
|
* unposted: Etc/BUGS: HIST_IGNORE_DUPS mishandles quoted whitespace.
|
||||||
|
|
||||||
* 52752: Src/params.c, Test/B02typeset.ztst: more typeset -p fixes
|
* 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
|
standard output. Like `tt(${|)...tt(})' and unlike
|
||||||
`tt($LPAR())...tt(RPAR())', the command executes in the current shell
|
`tt($LPAR())...tt(RPAR())', the command executes in the current shell
|
||||||
context with function local behaviors and does not create a subshell.
|
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
|
Note that because the `tt(${|)...tt(})' and `tt(${ )...tt( })' forms
|
||||||
must be parsed at once as both string tokens and commands, all other
|
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))
|
mytt(set -- pos1 pos2 etc). Nothing that happens within mytt($(command))
|
||||||
affects the caller.
|
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
|
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
|
split on tt(IFS) into an array of words. In contrast, and unlike both
|
||||||
bash and ksh, unquoted non-forking substitutions behave like parameter
|
bash and ksh, unquoted non-forking substitutions behave like parameter
|
||||||
expansions with respect to the tt(SH_WORD_SPLIT) option.
|
expansions with respect to the tt(SH_WORD_SPLIT) option.
|
||||||
|
|
||||||
When mytt(command) is myem(not) a builtin, mytt(${ command }) does fork, and
|
Both of the mytt(${|...}) formats retain any trailing newlines,
|
||||||
typically forks the same number of times as mytt($(command)), because in
|
except as handled by the tt(SH_WORD_SPLIT) option, consistent with
|
||||||
the latter case zsh usually optimizes the final fork into an exec.
|
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:
|
Redirecting input from files has subtle differences:
|
||||||
itemization(
|
itemization(
|
||||||
|
|
|
@ -1900,6 +1900,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
|
||||||
/* The command string to be run by ${|...;} */
|
/* The command string to be run by ${|...;} */
|
||||||
char *cmdarg = NULL;
|
char *cmdarg = NULL;
|
||||||
size_t slen = 0;
|
size_t slen = 0;
|
||||||
|
int trim = (!EMULATION(EMULATE_ZSH)) ? 2 : !qt;
|
||||||
inbrace = 1;
|
inbrace = 1;
|
||||||
s++;
|
s++;
|
||||||
|
|
||||||
|
@ -2005,10 +2006,13 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
|
||||||
int onoerrs = noerrs, rplylen;
|
int onoerrs = noerrs, rplylen;
|
||||||
noerrs = 2;
|
noerrs = 2;
|
||||||
rplylen = zstuff(&cmdarg, rplytmp);
|
rplylen = zstuff(&cmdarg, rplytmp);
|
||||||
if (! EMULATION(EMULATE_ZSH)) {
|
if (trim) {
|
||||||
/* bash and ksh strip trailing newlines here */
|
/* bash and ksh strip trailing newlines here */
|
||||||
while (rplylen > 0 && cmdarg[rplylen-1] == '\n')
|
while (rplylen > 0 && cmdarg[rplylen-1] == '\n') {
|
||||||
rplylen--;
|
rplylen--;
|
||||||
|
if (trim == 1)
|
||||||
|
break;
|
||||||
|
}
|
||||||
cmdarg[rplylen] = 0;
|
cmdarg[rplylen] = 0;
|
||||||
}
|
}
|
||||||
noerrs = onoerrs;
|
noerrs = onoerrs;
|
||||||
|
|
|
@ -86,9 +86,39 @@ F:setting option inside is too late for that substitution
|
||||||
?(eval):8: no matches found: f?*
|
?(eval):8: no matches found: f?*
|
||||||
|
|
||||||
purr ${| REPLY=$'trailing newlines remain\n\n' }
|
purr ${| REPLY=$'trailing newlines remain\n\n' }
|
||||||
0:newline removal should not occur
|
0:newline removal should not occur, part 1
|
||||||
>trailing newlines remain
|
>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+
|
1:unbalanced braces, part 4+
|
||||||
?(eval):1: closing brace expected
|
?(eval):1: closing brace expected
|
||||||
|
|
||||||
purr ${ purr STDOUT }
|
purr "${ purr STDOUT }"
|
||||||
0:capture stdout
|
0:capture stdout
|
||||||
>STDOUT
|
>STDOUT
|
||||||
>
|
>
|
||||||
|
@ -322,7 +352,7 @@ F:Fiddly here to get EOF past the test syntax
|
||||||
0:here-string behavior
|
0:here-string behavior
|
||||||
>in a here string
|
>in a here string
|
||||||
|
|
||||||
<<<${ purr $'stdout as a here string' }
|
<<<"${ purr $'stdout as a here string' }"
|
||||||
0:another capture stdout
|
0:another capture stdout
|
||||||
>stdout as a here string
|
>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
|
wrap=${ purr "capture in environment assignment" } typeset -p wrap
|
||||||
0:assignment context
|
0:assignment context
|
||||||
>typeset -g wrap='REPLY in environment assignment'
|
>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
|
# 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
|
0:ignored braces, part 1
|
||||||
>buried}
|
>buried}
|
||||||
|
|
||||||
purr ${ purr ${REPLY:-buried}}}
|
purr "${ purr ${REPLY:-buried}}}"
|
||||||
0:ignored braces, part 2
|
0:ignored braces, part 2
|
||||||
>buried
|
>buried
|
||||||
>}
|
>}
|
||||||
|
@ -418,7 +448,6 @@ F:must do this before evaluating the next test block
|
||||||
purr ${ { echo nested ;} }
|
purr ${ { echo nested ;} }
|
||||||
0:ignored braces, part 3
|
0:ignored braces, part 3
|
||||||
>nested
|
>nested
|
||||||
>
|
|
||||||
|
|
||||||
purr ${ { echo nested } } DONE
|
purr ${ { echo nested } } DONE
|
||||||
1:ignored braces, part 4
|
1:ignored braces, part 4
|
||||||
|
|
Loading…
Reference in New Issue