xref: /titanic_50/usr/src/lib/libshell/common/tests/basic.sh (revision 3e14f97f673e8a630f076077de35afdd43dc1587)
1da2e3ebdSchin########################################################################
2da2e3ebdSchin#                                                                      #
3da2e3ebdSchin#               This software is part of the ast package               #
4*3e14f97fSRoger A. Faulkner#          Copyright (c) 1982-2010 AT&T Intellectual Property          #
5da2e3ebdSchin#                      and is licensed under the                       #
6da2e3ebdSchin#                  Common Public License, Version 1.0                  #
77c2fbfb3SApril Chin#                    by AT&T Intellectual Property                     #
8da2e3ebdSchin#                                                                      #
9da2e3ebdSchin#                A copy of the License is available at                 #
10da2e3ebdSchin#            http://www.opensource.org/licenses/cpl1.0.txt             #
11da2e3ebdSchin#         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         #
12da2e3ebdSchin#                                                                      #
13da2e3ebdSchin#              Information and Software Systems Research               #
14da2e3ebdSchin#                            AT&T Research                             #
15da2e3ebdSchin#                           Florham Park NJ                            #
16da2e3ebdSchin#                                                                      #
17da2e3ebdSchin#                  David Korn <dgk@research.att.com>                   #
18da2e3ebdSchin#                                                                      #
19da2e3ebdSchin########################################################################
20da2e3ebdSchinfunction err_exit
21da2e3ebdSchin{
22da2e3ebdSchin	print -u2 -n "\t"
23da2e3ebdSchin	print -u2 -r ${Command}[$1]: "${@:2}"
24da2e3ebdSchin	let Errors+=1
25da2e3ebdSchin}
26da2e3ebdSchinalias err_exit='err_exit $LINENO'
27da2e3ebdSchin
28da2e3ebdSchinCommand=${0##*/}
29da2e3ebdSchininteger Errors=0
3034f9b3eeSRoland Mainz
3134f9b3eeSRoland Mainztmp=$(mktemp -dt) || { err_exit mktemp -dt failed; exit 1; }
3234f9b3eeSRoland Mainztrap "cd /; rm -rf $tmp" EXIT
3334f9b3eeSRoland Mainz
3434f9b3eeSRoland Mainz# test basic file operations like redirection, pipes, file expansion
357c2fbfb3SApril Chinset -- \
367c2fbfb3SApril Chin	go+r	0000	\
377c2fbfb3SApril Chin	go-r	0044	\
387c2fbfb3SApril Chin	ug=r	0330	\
397c2fbfb3SApril Chin	go+w	0000	\
407c2fbfb3SApril Chin	go-w	0022	\
417c2fbfb3SApril Chin	ug=w	0550	\
427c2fbfb3SApril Chin	go+x	0000	\
437c2fbfb3SApril Chin	go-x	0011	\
447c2fbfb3SApril Chin	ug=x	0660	\
457c2fbfb3SApril Chin	go-rx	0055	\
467c2fbfb3SApril Chin	uo-wx	0303	\
477c2fbfb3SApril Chin	ug-rw	0660	\
487c2fbfb3SApril Chin	o=	0007
497c2fbfb3SApril Chinwhile	(( $# >= 2 ))
507c2fbfb3SApril Chindo	umask 0
517c2fbfb3SApril Chin	umask $1
527c2fbfb3SApril Chin	g=$(umask)
537c2fbfb3SApril Chin	[[ $g == $2 ]] || err_exit "umask 0; umask $1 failed -- expected $2, got $g"
547c2fbfb3SApril Chin	shift 2
557c2fbfb3SApril Chindone
56da2e3ebdSchinumask u=rwx,go=rx || err_exit "umask u=rws,go=rx failed"
57da2e3ebdSchinif	[[ $(umask -S) != u=rwx,g=rx,o=rx ]]
58da2e3ebdSchinthen	err_exit 'umask -S incorrect'
59da2e3ebdSchinfi
60da2e3ebdSchinpwd=$PWD
61da2e3ebdSchin[[ $SHELL != /* ]] && SHELL=$pwd/$SHELL
6234f9b3eeSRoland Mainzcd $tmp || { err_exit "cd $tmp failed"; exit 1; }
637c2fbfb3SApril Chinum=$(umask -S)
647c2fbfb3SApril Chin( umask 0777; > foobar )
657c2fbfb3SApril Chinrm -f foobar
667c2fbfb3SApril Chin> foobar
677c2fbfb3SApril Chin[[ -r foobar ]] || err_exit 'umask not being restored after subshell'
687c2fbfb3SApril Chinumask "$um"
697c2fbfb3SApril Chinrm -f foobar
70da2e3ebdSchin# optimizer bug test
71da2e3ebdSchin> foobar
72da2e3ebdSchinfor i in 1 2
73da2e3ebdSchindo      print foobar*
74da2e3ebdSchin        rm -f foobar
7534f9b3eeSRoland Mainzdone > out
7634f9b3eeSRoland Mainzif      [[ "$(<out)"  != "foobar"$'\n'"foobar*" ]]
77da2e3ebdSchinthen    print -u2 "optimizer bug with file expansion"
78da2e3ebdSchinfi
7934f9b3eeSRoland Mainzrm -f out foobar
80da2e3ebdSchinmkdir dir
81da2e3ebdSchinif	[[ $(print */) != dir/ ]]
82da2e3ebdSchinthen	err_exit 'file expansion with trailing / not working'
83da2e3ebdSchinfi
84da2e3ebdSchinif	[[ $(print *) != dir ]]
85da2e3ebdSchinthen	err_exit 'file expansion with single file not working'
86da2e3ebdSchinfi
87da2e3ebdSchinprint hi > .foo
88da2e3ebdSchinif	[[ $(print *) != dir ]]
89da2e3ebdSchinthen	err_exit 'file expansion leading . not working'
90da2e3ebdSchinfi
91da2e3ebdSchindate > dat1 || err_exit "date > dat1 failed"
92da2e3ebdSchintest -r dat1 || err_exit "dat1 is not readable"
93da2e3ebdSchinx=dat1
94da2e3ebdSchincat <$x > dat2 || err_exit "cat < $x > dat2 failed"
95da2e3ebdSchincat dat1 dat2 | cat  | cat | cat > dat3 || err_exit "cat pipe failed"
96da2e3ebdSchincat > dat4 <<!
97da2e3ebdSchin$(date)
98da2e3ebdSchin!
99da2e3ebdSchincat dat1 dat2 | cat  | cat | cat > dat5 &
100da2e3ebdSchinwait $!
101da2e3ebdSchinset -- dat*
102da2e3ebdSchinif	(( $# != 5 ))
103da2e3ebdSchinthen	err_exit "dat* matches only $# files"
104da2e3ebdSchinfi
105da2e3ebdSchinif	(command > foo\\abc) 2> /dev/null
106da2e3ebdSchinthen	set -- foo*
107da2e3ebdSchin	if	[[ $1 != 'foo\abc' ]]
108da2e3ebdSchin	then	err_exit 'foo* does not match foo\abc'
109da2e3ebdSchin	fi
110da2e3ebdSchinfi
111da2e3ebdSchinif ( : > TT* && : > TTfoo ) 2>/dev/null
112da2e3ebdSchinthen	set -- TT*
113da2e3ebdSchin	if	(( $# < 2 ))
114da2e3ebdSchin	then	err_exit 'TT* not expanding when file TT* exists'
115da2e3ebdSchin	fi
116da2e3ebdSchinfi
117da2e3ebdSchincd ~- || err_exit "cd back failed"
11834f9b3eeSRoland Mainzcat > $tmp/script <<- !
119da2e3ebdSchin	#! $SHELL
120da2e3ebdSchin	print -r -- \$0
121da2e3ebdSchin!
12234f9b3eeSRoland Mainzchmod 755 $tmp/script
12334f9b3eeSRoland Mainzif	[[ $($tmp/script) != "$tmp/script" ]]
124da2e3ebdSchinthen	err_exit '$0 not correct for #! script'
125da2e3ebdSchinfi
126da2e3ebdSchinbar=foo
127da2e3ebdSchineval foo=\$bar
128da2e3ebdSchinif	[[ $foo != foo ]]
129da2e3ebdSchinthen	err_exit 'eval foo=\$bar not working'
130da2e3ebdSchinfi
131da2e3ebdSchinbar='foo=foo\ bar'
132da2e3ebdSchineval $bar
133da2e3ebdSchinif	[[ $foo != 'foo bar' ]]
134da2e3ebdSchinthen	err_exit 'eval foo=\$bar, with bar="foo\ bar" not working'
135da2e3ebdSchinfi
136da2e3ebdSchincd /tmp
137da2e3ebdSchincd ../../tmp || err_exit "cd ../../tmp failed"
138da2e3ebdSchinif	[[ $PWD != /tmp ]]
139da2e3ebdSchinthen	err_exit 'cd ../../tmp is not /tmp'
140da2e3ebdSchinfi
141da2e3ebdSchin( sleep 2; cat <<!
142da2e3ebdSchinfoobar
143da2e3ebdSchin!
14434f9b3eeSRoland Mainz) | cat > $tmp/foobar &
145da2e3ebdSchinwait $!
14634f9b3eeSRoland Mainzfoobar=$( < $tmp/foobar)
147da2e3ebdSchinif	[[ $foobar != foobar ]]
148da2e3ebdSchinthen	err_exit "$foobar is not foobar"
149da2e3ebdSchinfi
150da2e3ebdSchin{
151da2e3ebdSchin	print foo
152da2e3ebdSchin	/bin/echo bar
153da2e3ebdSchin	print bam
15434f9b3eeSRoland Mainz} > $tmp/foobar
15534f9b3eeSRoland Mainzif	[[ $( < $tmp/foobar) != $'foo\nbar\nbam' ]]
15634f9b3eeSRoland Mainzthen	err_exit "output file pointer not shared correctly"
157da2e3ebdSchinfi
15834f9b3eeSRoland Mainzcat > $tmp/foobar <<\!
159da2e3ebdSchin	print foo
160da2e3ebdSchin	/bin/echo bar
161da2e3ebdSchin	print bam
162da2e3ebdSchin!
16334f9b3eeSRoland Mainzchmod +x $tmp/foobar
16434f9b3eeSRoland Mainzif	[[ $($tmp/foobar) != $'foo\nbar\nbam' ]]
16534f9b3eeSRoland Mainzthen	err_exit "script not working"
166da2e3ebdSchinfi
16734f9b3eeSRoland Mainzif	[[ $($tmp/foobar | /bin/cat) != $'foo\nbar\nbam' ]]
16834f9b3eeSRoland Mainzthen	err_exit "script | cat not working"
169da2e3ebdSchinfi
17034f9b3eeSRoland Mainzif	[[ $( $tmp/foobar) != $'foo\nbar\nbam' ]]
17134f9b3eeSRoland Mainzthen	err_exit "output file pointer not shared correctly"
172da2e3ebdSchinfi
17334f9b3eeSRoland Mainzrm -f $tmp/foobar
174da2e3ebdSchinx=$( (print foo) ; (print bar) )
175da2e3ebdSchinif	[[ $x != $'foo\nbar' ]]
176da2e3ebdSchinthen	err_exit " ( (print foo);(print bar ) failed"
177da2e3ebdSchinfi
178da2e3ebdSchinx=$( (/bin/echo foo) ; (print bar) )
179da2e3ebdSchinif	[[ $x != $'foo\nbar' ]]
180da2e3ebdSchinthen	err_exit " ( (/bin/echo);(print bar ) failed"
181da2e3ebdSchinfi
182da2e3ebdSchinx=$( (/bin/echo foo) ; (/bin/echo bar) )
183da2e3ebdSchinif	[[ $x != $'foo\nbar' ]]
184da2e3ebdSchinthen	err_exit " ( (/bin/echo);(/bin/echo bar ) failed"
185da2e3ebdSchinfi
18634f9b3eeSRoland Mainzcat > $tmp/script <<\!
187da2e3ebdSchinif	[[ -p /dev/fd/0 ]]
188da2e3ebdSchinthen	builtin cat
189da2e3ebdSchin	cat - > /dev/null
190da2e3ebdSchin	[[ -p /dev/fd/0 ]] && print ok
191da2e3ebdSchinelse	print no
192da2e3ebdSchinfi
193da2e3ebdSchin!
19434f9b3eeSRoland Mainzchmod +x $tmp/script
19534f9b3eeSRoland Mainzcase $( (print) | $tmp/script;:) in
196da2e3ebdSchinok)	;;
197da2e3ebdSchinno)	err_exit "[[ -p /dev/fd/0 ]] fails for standard input pipe" ;;
198da2e3ebdSchin*)	err_exit "builtin replaces standard input pipe" ;;
199da2e3ebdSchinesac
20034f9b3eeSRoland Mainzprint 'print $0' > $tmp/script
20134f9b3eeSRoland Mainzprint ". $tmp/script" > $tmp/scriptx
20234f9b3eeSRoland Mainzchmod +x $tmp/scriptx
20334f9b3eeSRoland Mainzif	[[ $($tmp/scriptx) != $tmp/scriptx ]]
204da2e3ebdSchinthen	err_exit '$0 not correct for . script'
205da2e3ebdSchinfi
20634f9b3eeSRoland Mainzcd $tmp || { err_exit "cd $tmp failed"; exit 1; }
207da2e3ebdSchinprint ./b > ./a; print ./c > b; print ./d > c; print ./e > d; print "echo \"hello there\"" > e
208da2e3ebdSchinchmod 755 a b c d e
209da2e3ebdSchinx=$(./a)
210da2e3ebdSchinif	[[ $x != "hello there" ]]
211da2e3ebdSchinthen	err_exit "nested scripts failed"
212da2e3ebdSchinfi
213da2e3ebdSchinx=$( (./a) | cat)
214da2e3ebdSchinif	[[ $x != "hello there" ]]
215da2e3ebdSchinthen	err_exit "scripts in subshells fail"
216da2e3ebdSchinfi
217da2e3ebdSchincd ~- || err_exit "cd back failed"
218da2e3ebdSchinx=$( (/bin/echo foo) 2> /dev/null )
219da2e3ebdSchinif	[[ $x != foo ]]
220da2e3ebdSchinthen	err_exit "subshell in command substitution fails"
221da2e3ebdSchinfi
222da2e3ebdSchinexec 1>&-
223da2e3ebdSchinx=$(print hello)
224da2e3ebdSchinif	[[ $x != hello ]]
225da2e3ebdSchinthen	err_exit "command subsitution with stdout closed failed"
226da2e3ebdSchinfi
227da2e3ebdSchincd $pwd
228da2e3ebdSchinx=$(cat <<\! | $SHELL
229da2e3ebdSchin/bin/echo | /bin/cat
230da2e3ebdSchin/bin/echo hello
231da2e3ebdSchin!
232da2e3ebdSchin)
233da2e3ebdSchinif	[[ $x != $'\n'hello ]]
234da2e3ebdSchinthen	err_exit "$SHELL not working when standard input is a pipe"
235da2e3ebdSchinfi
236da2e3ebdSchinx=$( (/bin/echo hello) 2> /dev/null )
237da2e3ebdSchinif	[[ $x != hello ]]
238da2e3ebdSchinthen	err_exit "subshell in command substitution with 1 closed fails"
239da2e3ebdSchinfi
24034f9b3eeSRoland Mainzcat > $tmp/script <<- \!
241da2e3ebdSchinread line 2> /dev/null
242da2e3ebdSchinprint done
243da2e3ebdSchin!
24434f9b3eeSRoland Mainzif	[[ $($SHELL $tmp/script <&-) != done ]]
245da2e3ebdSchinthen	err_exit "executing script with 0 closed fails"
246da2e3ebdSchinfi
247da2e3ebdSchintrap '' INT
24834f9b3eeSRoland Mainzcat > $tmp/script <<- \!
249da2e3ebdSchintrap 'print bad' INT
250da2e3ebdSchinkill -s INT $$
251da2e3ebdSchinprint good
252da2e3ebdSchin!
25334f9b3eeSRoland Mainzchmod +x $tmp/script
25434f9b3eeSRoland Mainzif	[[ $($SHELL  $tmp/script) != good ]]
255da2e3ebdSchinthen	err_exit "traps ignored by parent not ignored"
256da2e3ebdSchinfi
257da2e3ebdSchintrap - INT
25834f9b3eeSRoland Mainzcat > $tmp/script <<- \!
259da2e3ebdSchinread line
260da2e3ebdSchin/bin/cat
261da2e3ebdSchin!
26234f9b3eeSRoland Mainzif	[[ $($SHELL $tmp/script <<!
263da2e3ebdSchinone
264da2e3ebdSchintwo
265da2e3ebdSchin!
266da2e3ebdSchin)	!= two ]]
267da2e3ebdSchinthen	err_exit "standard input not positioned correctly"
268da2e3ebdSchinfi
269da2e3ebdSchinword=$(print $'foo\nbar' | { read line; /bin/cat;})
270da2e3ebdSchinif	[[ $word != bar ]]
271da2e3ebdSchinthen	err_exit "pipe to { read line; /bin/cat;} not working"
272da2e3ebdSchinfi
273da2e3ebdSchinword=$(print $'foo\nbar' | ( read line; /bin/cat) )
274da2e3ebdSchinif	[[ $word != bar ]]
275da2e3ebdSchinthen	err_exit "pipe to ( read line; /bin/cat) not working"
276da2e3ebdSchinfi
277da2e3ebdSchinif	[[ $(print x{a,b}y) != 'xay xby' ]]
278da2e3ebdSchinthen	err_exit 'brace expansion not working'
279da2e3ebdSchinfi
280da2e3ebdSchinif	[[ $(for i in foo bar
281da2e3ebdSchin	  do ( tgz=$(print $i)
282da2e3ebdSchin	  print $tgz)
283da2e3ebdSchin	  done) != $'foo\nbar' ]]
284da2e3ebdSchinthen	err_exit 'for loop subshell optimizer bug'
285da2e3ebdSchinfi
286da2e3ebdSchinunset a1
287da2e3ebdSchinoptbug()
288da2e3ebdSchin{
289da2e3ebdSchin	set -A a1  foo bar bam
290da2e3ebdSchin	integer i
291da2e3ebdSchin	for ((i=0; i < 3; i++))
292da2e3ebdSchin	do
293da2e3ebdSchin		(( ${#a1[@]} < 2 )) && return 0
294da2e3ebdSchin		set -- "${a1[@]}"
295da2e3ebdSchin		shift
296da2e3ebdSchin		set -A a1 -- "$@"
297da2e3ebdSchin	done
298da2e3ebdSchin	return 1
299da2e3ebdSchin}
300da2e3ebdSchinoptbug ||  err_exit 'array size optimzation bug'
3017c2fbfb3SApril Chinwait # not running --pipefail which would interfere with subsequent tests
302da2e3ebdSchin: $(jobs -p) # required to clear jobs for next jobs -p (interactive side effect)
303da2e3ebdSchinsleep 20 &
3047c2fbfb3SApril Chinpids=$!
305da2e3ebdSchinif	[[ $(jobs -p) != $! ]]
306da2e3ebdSchinthen	err_exit 'jobs -p not reporting a background job'
307da2e3ebdSchinfi
308da2e3ebdSchinsleep 20 &
3097c2fbfb3SApril Chinpids="$pids $!"
310da2e3ebdSchinfoo()
311da2e3ebdSchin{
312da2e3ebdSchin	set -- $(jobs -p)
313da2e3ebdSchin	(( $# == 2 )) || err_exit "$# jobs not reported -- 2 expected"
314da2e3ebdSchin}
315da2e3ebdSchinfoo
3167c2fbfb3SApril Chinkill $pids
3177c2fbfb3SApril Chin
3187c2fbfb3SApril Chin[[ $( (trap 'print alarm' ALRM; sleep 4) & sleep 2; kill -ALRM $!; sleep 2; wait) == alarm ]] || err_exit 'ALRM signal not working'
319da2e3ebdSchin[[ $($SHELL -c 'trap "" HUP; $SHELL -c "(sleep 2;kill -HUP $$)& sleep 4;print done"') != done ]] && err_exit 'ignored traps not being ignored'
320da2e3ebdSchin[[ $($SHELL -c 'o=foobar; for x in foo bar; do (o=save);print $o;done' 2> /dev/null ) == $'foobar\nfoobar' ]] || err_exit 'for loop optimization subshell bug'
321da2e3ebdSchincommand exec 3<> /dev/null
322da2e3ebdSchinif	cat /dev/fd/3 >/dev/null 2>&1
323da2e3ebdSchinthen	[[ $($SHELL -c 'cat <(print foo)' 2> /dev/null) == foo ]] || err_exit 'process substitution not working'
32434f9b3eeSRoland Mainz	[[ $($SHELL -c  $'tee >(grep \'1$\' > '$tmp/scriptx$') > /dev/null <<-  \!!!
325da2e3ebdSchin	line0
326da2e3ebdSchin	line1
327da2e3ebdSchin	line2
328da2e3ebdSchin	!!!
329da2e3ebdSchin	wait
33034f9b3eeSRoland Mainz	cat '$tmp/scriptx 2> /dev/null)  == line1 ]] || err_exit '>() process substitution fails'
33134f9b3eeSRoland Mainz	> $tmp/scriptx
332da2e3ebdSchin	[[ $($SHELL -c  $'
333da2e3ebdSchin	for i in 1
33434f9b3eeSRoland Mainz	do	tee >(grep \'1$\' > '$tmp/scriptx$') > /dev/null  <<-  \!!!
335da2e3ebdSchin		line0
336da2e3ebdSchin		line1
337da2e3ebdSchin		line2
338da2e3ebdSchin		!!!
339da2e3ebdSchin	done
340da2e3ebdSchin	wait
34134f9b3eeSRoland Mainz	cat '$tmp/scriptx 2>> /dev/null) == line1 ]] || err_exit '>() process substitution fails in for loop'
342da2e3ebdSchin	[[ $({ $SHELL -c 'cat <(for i in x y z; do print $i; done)';} 2> /dev/null) == $'x\ny\nz' ]] ||
343da2e3ebdSchin		err_exit 'process substitution of compound commands not working'
344da2e3ebdSchinfi
345da2e3ebdSchin[[ $($SHELL -r 'command -p :' 2>&1) == *restricted* ]]  || err_exit 'command -p not restricted'
34634f9b3eeSRoland Mainzprint cat >  $tmp/scriptx
34734f9b3eeSRoland Mainzchmod +x $tmp/scriptx
34834f9b3eeSRoland Mainz[[ $($SHELL -c "print foo | $tmp/scriptx ;:" 2> /dev/null ) == foo ]] || err_exit 'piping into script fails'
349da2e3ebdSchin[[ $($SHELL -c 'X=1;print -r -- ${X:=$(expr "a(0)" : '"'a*(\([^)]\))')}'" 2> /dev/null) == 1 ]] || err_exit 'x=1;${x:=$(..."...")} failure'
350da2e3ebdSchin[[ $($SHELL -c 'print -r -- ${X:=$(expr "a(0)" : '"'a*(\([^)]\))')}'" 2> /dev/null) == 0 ]] || err_exit '${x:=$(..."...")} failure'
3517c2fbfb3SApril Chinexec 3<&-
352da2e3ebdSchinif	[[ -d /dev/fd  && -w /dev/fd/3 ]]
353da2e3ebdSchinthen	[[ $(cat <(print hello) ) == hello ]] || err_exit "process substitution not working outside for or while loop"
354da2e3ebdSchin	$SHELL -c '[[ $(for i in 1;do cat <(print hello);done ) == hello ]]' 2> /dev/null|| err_exit "process substitution not working in for or while loop"
355da2e3ebdSchinfi
356da2e3ebdSchinexec 3> /dev/null
35734f9b3eeSRoland Mainzprint 'print foo "$@"' > $tmp/scriptx
35834f9b3eeSRoland Mainz[[ $( print "($tmp/scriptx bar)" | $SHELL 2>/dev/null) == 'foo bar' ]] || err_exit 'script pipe to shell fails'
35934f9b3eeSRoland Mainzprint "#! $SHELL" > $tmp/scriptx
36034f9b3eeSRoland Mainzprint 'print  -- $0' >> $tmp/scriptx
36134f9b3eeSRoland Mainzchmod +x $tmp/scriptx
36234f9b3eeSRoland Mainz[[ $($tmp/scriptx) == $tmp/scriptx ]] || err_exit  "\$0 is $0 instead of $tmp/scriptx"
36334f9b3eeSRoland Mainzcat > $tmp/scriptx <<- \EOF
3647c2fbfb3SApril Chin	myfilter() { x=$(print ok | cat); print  -r -- $SECONDS;}
3657c2fbfb3SApril Chin	set -o pipefail
3667c2fbfb3SApril Chin	sleep 3 | myfilter
3677c2fbfb3SApril ChinEOF
36834f9b3eeSRoland Mainz(( $($SHELL $tmp/scriptx) > 2.0 )) && err_exit 'command substitution causes pipefail option to hang'
369da2e3ebdSchinexec 3<&-
370da2e3ebdSchin( typeset -r foo=bar) 2> /dev/null || err_exit 'readonly variables set in a subshell cannot unset'
3717c2fbfb3SApril Chin$SHELL -c 'x=${ print hello;}; [[ $x == hello ]]' 2> /dev/null || err_exit '${ command;} not supported'
3727c2fbfb3SApril Chin$SHELL 2> /dev/null <<- \EOF || err_exit 'multiline ${...} command substitution not supported'
3737c2fbfb3SApril Chin	x=${
3747c2fbfb3SApril Chin		print hello
3757c2fbfb3SApril Chin	}
3767c2fbfb3SApril Chin	[[ $x == hello ]]
3777c2fbfb3SApril ChinEOF
3787c2fbfb3SApril Chin$SHELL 2> /dev/null <<- \EOF || err_exit '${...} command substitution with side effects not supported '
3797c2fbfb3SApril Chin	y=bye
3807c2fbfb3SApril Chin	x=${
3817c2fbfb3SApril Chin		y=hello
3827c2fbfb3SApril Chin		print hello
3837c2fbfb3SApril Chin	}
3847c2fbfb3SApril Chin	[[ $y == $x ]]
3857c2fbfb3SApril ChinEOF
3867c2fbfb3SApril Chin$SHELL   2> /dev/null <<- \EOF || err_exit 'nested ${...} command substitution not supported'
3877c2fbfb3SApril Chin	x=${
3887c2fbfb3SApril Chin		print ${ print hello;} $(print world)
3897c2fbfb3SApril Chin	}
3907c2fbfb3SApril Chin	[[ $x == 'hello world' ]]
3917c2fbfb3SApril ChinEOF
3927c2fbfb3SApril Chin$SHELL   2> /dev/null <<- \EOF || err_exit 'terminating } is not a reserved word with ${ command }'
3937c2fbfb3SApril Chin	x=${	{ print -n } ; print -n hello ; }  ; print ' world' }
3947c2fbfb3SApril Chin	[[ $x == '}hello world' ]]
3957c2fbfb3SApril ChinEOF
3967c2fbfb3SApril Chin$SHELL   2> /dev/null <<- \EOF || err_exit '${ command;}xxx not working'
3977c2fbfb3SApril Chin	f()
3987c2fbfb3SApril Chin	{
3997c2fbfb3SApril Chin		print foo
4007c2fbfb3SApril Chin	}
4017c2fbfb3SApril Chin	[[ ${ f;}bar == foobar ]]
4027c2fbfb3SApril ChinEOF
4037c2fbfb3SApril Chin
4047c2fbfb3SApril Chinunset foo
4057c2fbfb3SApril Chin[[ ! ${foo[@]} ]] || err_exit '${foo[@]} is not empty when foo is unset'
4067c2fbfb3SApril Chin[[ ! ${foo[3]} ]] || err_exit '${foo[3]} is not empty when foo is unset'
4077c2fbfb3SApril Chin[[ $(print  "[${ print foo }]") == '[foo]' ]] || err_exit '${...} not working when } is followed by ]'
4087c2fbfb3SApril Chin[[ $(print  "${ print "[${ print foo }]" }") == '[foo]' ]] || err_exit 'nested ${...} not working when } is followed by ]'
4097c2fbfb3SApril Chinunset foo
4107c2fbfb3SApril Chinfoo=$(false) > /dev/null && err_exit 'failed command substitution with redirection not returning false'
4117c2fbfb3SApril Chinexpected=foreback
4127c2fbfb3SApril Chingot=$(print -n fore;(sleep 2;print back)&)
4137c2fbfb3SApril Chin[[ $got == $expected ]] || err_exit "command substitution background process output error -- got '$got', expected '$expected'"
4147c2fbfb3SApril Chin
41534f9b3eeSRoland Mainzbinfalse=$(whence -p false)
41634f9b3eeSRoland Mainzfor false in false $binfalse
4177c2fbfb3SApril Chindo	x=$($false) && err_exit "x=\$($false) should fail"
4187c2fbfb3SApril Chin	$($false) && err_exit "\$($false) should fail"
4197c2fbfb3SApril Chin	$($false) > /dev/null && err_exit "\$($false) > /dev/null should fail"
4207c2fbfb3SApril Chindone
42134f9b3eeSRoland Mainzif	env x-a=y >/dev/null 2>&1
42234f9b3eeSRoland Mainzthen	[[ $(env 'x-a=y'  $SHELL -c 'env | grep x-a') == *x-a=y* ]] || err_exit 'invalid environment variables not preserved'
42334f9b3eeSRoland Mainzfi
42434f9b3eeSRoland Mainzfloat s=SECONDS
42534f9b3eeSRoland Mainzsleep=$(whence -p sleep)
42634f9b3eeSRoland Mainzfor i in 1 2
42734f9b3eeSRoland Mainzdo      print $i
42834f9b3eeSRoland Mainzdone | while read sec; do ( $sleep $sec; $sleep $sec) done
42934f9b3eeSRoland Mainz(( (SECONDS-s)  < 4)) && err_exit '"command | while read...done" finishing too fast'
43034f9b3eeSRoland Mainzs=SECONDS
43134f9b3eeSRoland Mainzset -o pipefail
43234f9b3eeSRoland Mainzfor ((i=0; i < 30; i++))
43334f9b3eeSRoland Mainzdo	print hello
43434f9b3eeSRoland Mainz	sleep .1
43534f9b3eeSRoland Mainzdone |  $sleep 1
43634f9b3eeSRoland Mainz(( (SECONDS-s) < 2 )) || err_exit 'early termination not causing broken pipe'
43734f9b3eeSRoland Mainz[[ $({ trap 'print trap' 0; print -n | $(whence -p cat); } & wait $!) == trap ]] || err_exit 'trap on exit not getting triggered'
43834f9b3eeSRoland Mainzvar=$({ trap 'print trap' ERR; print -n | $binfalse; } & wait $!)
43934f9b3eeSRoland Mainz[[ $var == trap ]] || err_exit 'trap on ERR not getting triggered'
44034f9b3eeSRoland Mainz
44134f9b3eeSRoland Mainzexp=
44234f9b3eeSRoland Mainzgot=$(
44334f9b3eeSRoland Mainz	function fun
44434f9b3eeSRoland Mainz	{
44534f9b3eeSRoland Mainz		$binfalse && echo FAILED
44634f9b3eeSRoland Mainz	}
44734f9b3eeSRoland Mainz	: works if this line deleted : |
44834f9b3eeSRoland Mainz	fun
44934f9b3eeSRoland Mainz	: works if this line deleted :
45034f9b3eeSRoland Mainz)
45134f9b3eeSRoland Mainz[[ $got == $exp ]] || err_exit "pipe to function with conditional fails -- expected '$exp', got '$got'"
45234f9b3eeSRoland Mainzgot=$(
45334f9b3eeSRoland Mainz	: works if this line deleted : |
45434f9b3eeSRoland Mainz	{ $binfalse && echo FAILED; }
45534f9b3eeSRoland Mainz	: works if this line deleted :
45634f9b3eeSRoland Mainz)
45734f9b3eeSRoland Mainz[[ $got == $exp ]] || err_exit "pipe to { ... } with conditional fails -- expected '$exp', got '$got'"
45834f9b3eeSRoland Mainz
45934f9b3eeSRoland Mainzgot=$(
46034f9b3eeSRoland Mainz	: works if this line deleted : |
46134f9b3eeSRoland Mainz	( $binfalse && echo FAILED )
46234f9b3eeSRoland Mainz	: works if this line deleted :
46334f9b3eeSRoland Mainz)
46434f9b3eeSRoland Mainz[[ $got == $exp ]] || err_exit "pipe to ( ... ) with conditional fails -- expected '$exp', got '$got'"
46534f9b3eeSRoland Mainz
46634f9b3eeSRoland Mainz( $SHELL -c 'trap : DEBUG; x=( $foo); exit 0') 2> /dev/null  || err_exit 'trap DEBUG fails'
46734f9b3eeSRoland Mainz
468*3e14f97fSRoger A. Faulknertrue=$(whence -p true)
469*3e14f97fSRoger A. Faulknerset -o pipefail
470*3e14f97fSRoger A. Faulknerfloat start=$SECONDS end
471*3e14f97fSRoger A. Faulknerfor ((i=0; i < 2; i++))
472*3e14f97fSRoger A. Faulknerdo	print foo
473*3e14f97fSRoger A. Faulkner	sleep 1.5
474*3e14f97fSRoger A. Faulknerdone | { read; $true; end=$SECONDS ;}
475*3e14f97fSRoger A. Faulkner(( (SECONDS-start) < 1 )) && err_exit "pipefail not waiting for pipe to finish"
476*3e14f97fSRoger A. Faulknerset +o pipefail
477*3e14f97fSRoger A. Faulkner(( (SECONDS-start) > 2 )) &&  err_exit "pipefail causing /bin/true to wait for other end of pipe"
478*3e14f97fSRoger A. Faulkner
479*3e14f97fSRoger A. Faulkner
480*3e14f97fSRoger A. Faulkner{ env A__z=C+SHLVL $SHELL -c : ;} 2> /dev/null || err_exit "SHLVL with wrong attribute fails"
481*3e14f97fSRoger A. Faulkner
482da2e3ebdSchinexit $((Errors))
483