mirror of
git://git.code.sf.net/p/zsh/code
synced 2025-01-01 05:16:05 +01:00
515 lines
10 KiB
Text
515 lines
10 KiB
Text
# Tests for "nofork" command substitution.
|
|
|
|
%prep
|
|
mkdir nofork.tmp
|
|
touch nofork.tmp/file{1,2}.txt
|
|
|
|
purr() { print -r -- "$@" }
|
|
purl() { print -rl -- "$@" }
|
|
|
|
%test
|
|
|
|
REPLY=OUTER
|
|
purr ${| REPLY=INNER } $REPLY
|
|
0:Basic substitution and REPLY scoping
|
|
>INNER OUTER
|
|
|
|
reply=(x OUTER x)
|
|
purl ${{reply} reply=(\{ INNER \})} $reply
|
|
0:Basic substitution, brace quoting, and array result
|
|
>{
|
|
>INNER
|
|
>}
|
|
>{
|
|
>INNER
|
|
>}
|
|
|
|
() {
|
|
setopt localoptions ignorebraces
|
|
purl ${{reply} reply=({ INNER })} $reply
|
|
}
|
|
0:Basic substitution, ignorebraces, and array result
|
|
>{
|
|
>INNER
|
|
>}
|
|
>{
|
|
>INNER
|
|
>}
|
|
|
|
purr ${| REPLY=first}:${| REPLY=second}:$REPLY
|
|
0:re-scoping of REPLY in one statement
|
|
>first:second:OUTER
|
|
|
|
purr BEGIN${| printf -v REPLY '%s\n' one two three ; }END
|
|
0:Adjacent words
|
|
>BEGINone
|
|
>two
|
|
>three
|
|
>END
|
|
|
|
purr "BEGIN${| printf -v REPLY '%s\n' one two three }END"
|
|
0:Adjacent words and quoting, part 1
|
|
>BEGINone
|
|
>two
|
|
>three
|
|
>END
|
|
|
|
purr BEGIN"${| printf -v REPLY '%s\n' one two three }"END
|
|
0:Adjacent words and quoting, part 2
|
|
>BEGINone
|
|
>two
|
|
>three
|
|
>END
|
|
|
|
purr BEGIN"${|
|
|
printf -v REPLY '%s\n'\
|
|
one two three
|
|
}"END
|
|
0:Embedded newlines
|
|
>BEGINone
|
|
>two
|
|
>three
|
|
>END
|
|
|
|
purr BEGIN"${|
|
|
printf -v REPLY $'%s\n' one two three
|
|
}"END
|
|
0:Embedded newlines and $'...'
|
|
>BEGINone
|
|
>two
|
|
>three
|
|
>END
|
|
|
|
purl ${| print -v REPLY one word here; setopt shwordsplit }
|
|
purl ${| print -v REPLY three words here }
|
|
purl "and ${| print -v REPLY one word here }"
|
|
unsetopt shwordsplit
|
|
0:test word splitting on result
|
|
F:setting option inside is too late for that substitution
|
|
>one word here
|
|
>three
|
|
>words
|
|
>here
|
|
>and one word here
|
|
|
|
(
|
|
cd nofork.tmp
|
|
setopt globsubst
|
|
purr ${| REPLY=f* }
|
|
purr ${| REPLY=f? }*
|
|
unsetopt globsubst
|
|
purr ${| REPLY=f* }
|
|
purr ${| REPLY=f? }*
|
|
)
|
|
1:globsubst on result
|
|
>file1.txt file2.txt
|
|
>file1.txt file2.txt
|
|
>f*
|
|
?(eval):8: no matches found: f?*
|
|
|
|
purr ${| REPLY=$'trailing newlines remain\n\n' }
|
|
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
|
|
>
|
|
>
|
|
>
|
|
>
|
|
|
|
() {
|
|
purr ${| REPLY=$* ; shift 2 }
|
|
purr $*
|
|
} these are arguments
|
|
0:access to context $argv
|
|
>these are arguments
|
|
>arguments
|
|
|
|
purr ${:-${| REPLY=${REPLY:-buried}}}
|
|
purr ${:-"${| REPLY=${REPLY:-more buried}}"}
|
|
0:nofork inside parameter scope
|
|
>buried
|
|
>more buried
|
|
|
|
: ${(e):-'${| REPLY=oops'}
|
|
1:unclosed braces are sometimes a bad substitution
|
|
F:This seems silly, but see A01grammar ${(e):-'${'} test
|
|
?(eval):1: bad substitution
|
|
|
|
purr ${| REPLY=oops
|
|
1:other times lack of closing brace is merely unexpected
|
|
F:Why not use this error in the previous case as well?
|
|
?(eval):1: closing brace expected
|
|
|
|
# Next tests check that the PS2 stack is properly managed on error
|
|
|
|
purr ${| REPLY=${REPLY:-buried}}}
|
|
1:unbalanced braces, part 0
|
|
?(eval):1: parse error near `}'
|
|
|
|
purr ${:-${| REPLY=${REPLY:-buried}}
|
|
1:unbalanced braces, part 1
|
|
?(eval):1: closing brace expected
|
|
|
|
purr ${:-"${| REPLY=${REPLY:-more buried}"}
|
|
1:unbalanced braces, part 2
|
|
?(eval):1: unmatched "
|
|
|
|
purr ${:-"${| REPLY=${REPLY:-more buried"}}}
|
|
1:unbalanced braces, part 3
|
|
?(eval):1: unmatched "
|
|
|
|
purr ${:-"${| REPLY=${REPLY:-more buried}}}"
|
|
1:unbalanced braces, part 4
|
|
?(eval):1: closing brace expected
|
|
|
|
# Same tests with leading space (future-proofing)
|
|
|
|
purr ${ purr ${REPLY:-buried}}}
|
|
1:unbalanced braces, part 0+
|
|
?(eval):1: parse error near `}'
|
|
|
|
purr ${:-${ purr ${REPLY:-buried}}
|
|
1:unbalanced braces, part 1+
|
|
?(eval):1: closing brace expected
|
|
|
|
purr ${:-"${ purr ${REPLY:-more buried}"}
|
|
1:unbalanced braces, part 2+
|
|
?(eval):1: unmatched "
|
|
|
|
purr ${:-"${ purr ${REPLY:-more buried"}}}
|
|
1:unbalanced braces, part 3+
|
|
?(eval):1: unmatched "
|
|
|
|
purr ${:-"${ purr ${REPLY:-more buried}}}"
|
|
1:unbalanced braces, part 4+
|
|
?(eval):1: closing brace expected
|
|
|
|
purr "${ purr STDOUT }"
|
|
0:capture stdout
|
|
>STDOUT
|
|
>
|
|
|
|
# end PS2 stack tests
|
|
|
|
purr $(purr outside ${| REPLY=inside })
|
|
purr BEGIN$(purr outside ${| REPLY=inside })END
|
|
purr "BEGIN$(purr outside ${| REPLY=inside })END"
|
|
purr outside ${| REPLY=$(purr inside)}
|
|
purr "outside ${| REPLY=$(purr inside)}"
|
|
0:mixing with forking cmdsubst
|
|
>outside inside
|
|
>BEGINoutside insideEND
|
|
>BEGINoutside insideEND
|
|
>outside inside
|
|
>outside inside
|
|
|
|
purr `purr outside ${| REPLY=inside }`
|
|
purr "outside `purr ${| REPLY=inside }`"
|
|
purr outside ${| REPLY=`purr inside`}
|
|
purr "outside ${| REPLY=`purr inside`}"
|
|
purr outside "`purr ${| REPLY="${:-inside}"}`"
|
|
purr "outside ${| REPLY=`purr ${:-inside}`}"
|
|
0:mixing with backticks
|
|
>outside inside
|
|
>outside inside
|
|
>outside inside
|
|
>outside inside
|
|
>outside inside
|
|
>outside inside
|
|
|
|
purr ${| REPLY=$(( 9 + 17 )) }
|
|
purr $(( 9 + ${| REPLY=17 } ))
|
|
0:mixing with arithemetic
|
|
>26
|
|
>26
|
|
|
|
unset reply
|
|
purl ${{reply} reply=(1 2 ${| REPLY=3 } 4) }
|
|
typeset -p reply
|
|
0:array behavior with global assignment
|
|
>1
|
|
>2
|
|
>3
|
|
>4
|
|
>typeset -g -a reply=( 1 2 3 4 )
|
|
|
|
unset outer
|
|
purr "${|
|
|
outer=OUTER
|
|
REPLY=INNER
|
|
return 7
|
|
OUTER=NOTREACHED
|
|
} $outer $?"
|
|
0:return statement inside, part 1
|
|
F:status of "print" should hide return
|
|
>INNER OUTER 7
|
|
|
|
unset outer
|
|
outer=${| REPLY=${| return 7}}
|
|
7:return status propages in assignment like $(...)
|
|
|
|
unset outer
|
|
purr "${|
|
|
outer=OUTER
|
|
REPLY=INNER
|
|
return 7
|
|
OUTER=NOTREACHED
|
|
} $outer $?"
|
|
print REACHED $OUTER
|
|
0:return statement inside, part 2
|
|
>INNER OUTER 7
|
|
>REACHED
|
|
|
|
unset outer
|
|
purr "${|
|
|
# Localoptions needed to avoid breaking test harness?
|
|
# The setopt command affects surrounding context
|
|
setopt localoptions errreturn
|
|
outer=OUTER
|
|
REPLY=INNER
|
|
false
|
|
OUTER=NOTREACHED
|
|
} $outer $?"
|
|
print REACHED $OUTER ${options[errreturn]}
|
|
0:errreturn works inside and remains outside
|
|
>INNER OUTER 1
|
|
>REACHED on
|
|
|
|
(
|
|
unset outer
|
|
purr "${|
|
|
outer=OUTER
|
|
REPLY=INNER
|
|
exit 7
|
|
OUTER=NOTREACHED
|
|
} $outer $OUTER $?"
|
|
print NOT REACHED
|
|
)
|
|
7:exit statement inside
|
|
|
|
(
|
|
unset outer
|
|
purr "${|
|
|
setopt errexit
|
|
outer=OUTER
|
|
REPLY=INNER
|
|
false
|
|
OUTER=NOTREACHED
|
|
} $outer $?"
|
|
print NOT REACHED
|
|
)
|
|
1:errexit inside
|
|
|
|
outer=GLOBAL
|
|
purr "${|
|
|
local outer=LOCAL
|
|
REPLY=INNER
|
|
} $outer $?"
|
|
0:local declaration inside
|
|
>INNER GLOBAL 0
|
|
|
|
unset zz
|
|
outer=GLOBAL
|
|
purr "${{zz}
|
|
local outer=LOCAL
|
|
zz=NONLOCAL
|
|
} $outer $?"
|
|
print $zz
|
|
0:local declaration, global assignment, part 1
|
|
>NONLOCAL GLOBAL 0
|
|
>NONLOCAL
|
|
|
|
unset zz
|
|
outer=GLOBAL
|
|
purr "${${|
|
|
local outer=LOCAL
|
|
zz=NONLOCAL
|
|
}:-$zz} $outer $?"
|
|
0:local declaration, global assignment, part 2 (evaluation order)
|
|
>NONLOCAL GLOBAL 0
|
|
|
|
: ${| fn1() { () { print -v REPLY $'Defined Function' ;} ;} }
|
|
print "IN${| fn2() { () { print "${:-Second }${|fn1}" ;} ;} }OUT"
|
|
fn2
|
|
0:function definition, brace nesting, quote nesting
|
|
>INOUT
|
|
>Second Defined Function
|
|
|
|
<<-EOF
|
|
${| REPLY=$'in a here document\n' }
|
|
EOF
|
|
0:here-document behavior
|
|
F:Fiddly here to get EOF past the test syntax
|
|
>in a here document
|
|
>
|
|
|
|
<<<${| REPLY="in a here string" }
|
|
0:here-string behavior
|
|
>in a here string
|
|
|
|
<<<"${ purr $'stdout as a here string' }"
|
|
0:another capture stdout
|
|
>stdout as a here string
|
|
>
|
|
|
|
wrap=${| REPLY="REPLY in environment assignment" } typeset -p wrap
|
|
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'
|
|
|
|
# Repeat return and exit tests with stdout capture
|
|
|
|
purr "${
|
|
print INNER
|
|
return 7
|
|
} $?"
|
|
0:return statement inside, part 1+
|
|
F:status of "print" should hide return
|
|
>INNER
|
|
> 7
|
|
|
|
unset outer
|
|
outer=${ return 7 }
|
|
7:return status propages in stdout capture
|
|
|
|
unset outer
|
|
purr "${
|
|
outer=OUTER
|
|
print INNER
|
|
return 7
|
|
OUTER=NOTREACHED
|
|
} $outer $?"
|
|
print REACHED $OUTER
|
|
0:return statement inside, part 2+
|
|
>INNER
|
|
> OUTER 7
|
|
>REACHED
|
|
|
|
unset outer
|
|
purr "${
|
|
# Localoptions needed to avoid breaking test harness?
|
|
# The setopt command affects surrounding context
|
|
setopt localoptions errreturn
|
|
outer=OUTER
|
|
print INNER
|
|
false
|
|
OUTER=NOTREACHED
|
|
} $outer $?"
|
|
print REACHED $OUTER ${options[errreturn]}
|
|
0:errreturn works inside stdout capture
|
|
>INNER
|
|
> OUTER 1
|
|
>REACHED on
|
|
|
|
(
|
|
unset outer
|
|
purr "${
|
|
outer=OUTER
|
|
print INNER
|
|
exit 7
|
|
OUTER=NOTREACHED
|
|
} $outer $OUTER $?"
|
|
print NOT REACHED
|
|
)
|
|
7:exit statement inside stdout capture
|
|
|
|
(
|
|
unset outer
|
|
purr "${
|
|
setopt errexit
|
|
outer=OUTER
|
|
print INNER
|
|
false
|
|
OUTER=NOTREACHED
|
|
} $outer $?"
|
|
print NOT REACHED
|
|
)
|
|
1:errexit inside stdout capture
|
|
|
|
setopt ignorebraces
|
|
0:dummy test to set option soon enough
|
|
F:must do this before evaluating the next test block
|
|
|
|
purr ${| REPLY=${REPLY:-buried}}}
|
|
0:ignored braces, part 1
|
|
>buried}
|
|
|
|
# Global $REPLY still set from earlier test
|
|
purr "${ purr ${REPLY:+buried}}}"
|
|
0:ignored braces, part 2
|
|
>buried
|
|
>}
|
|
|
|
purr ${ { echo nested ;} }
|
|
0:ignored braces, part 3
|
|
>nested
|
|
|
|
purr ${ { echo nested } } DONE
|
|
1:ignored braces, part 4
|
|
?(eval):3: parse error near `}'
|
|
|
|
unsetopt ignorebraces
|
|
# "break" blocks function calls in outer loop
|
|
# Could use print, but that might get fixed
|
|
repeat 3 do purr ${
|
|
for x in 1 2 3 4
|
|
do (( x == 3 )) && break 2
|
|
# use error output to confirm loop count
|
|
print -u 2 $x
|
|
done
|
|
} XX
|
|
done
|
|
0:break N propagates
|
|
?1
|
|
?2
|
|
|
|
# Cannot "purr": break skips pending function calls
|
|
# Use "repeat" to avoid infinite loop on failure
|
|
repeat 3 do; echo ${|REPLY=x; break }; done
|
|
repeat 3 do; echo ${{x} x=y; break }; done
|
|
repeat 3 do; echo ${ echo z; break }; done
|
|
0:break after assignment completes the assignment
|
|
>x
|
|
>y
|
|
>z
|
|
|
|
# Subshell because error exits
|
|
( purr ${ echo ${unset?oops} } )
|
|
1:error handling (without crashing)
|
|
*?*unset: oops
|
|
|
|
purr ${ .zsh.cmdsubst=error }
|
|
1:reserved parameter name (without crashing)
|
|
*?*.zsh.cmdsubst: can't modify read-only parameter
|
|
|
|
%clean
|
|
|
|
unfunction purr purl
|