1########################################################################
2#                                                                      #
3#               This software is part of the ast package               #
4#          Copyright (c) 1982-2012 AT&T Intellectual Property          #
5#                      and is licensed under the                       #
6#                 Eclipse Public License, Version 1.0                  #
7#                    by AT&T Intellectual Property                     #
8#                                                                      #
9#                A copy of the License is available at                 #
10#          http://www.eclipse.org/org/documents/epl-v10.html           #
11#         (with md5 checksum b35adb5213ca9657e911e9befb180842)         #
12#                                                                      #
13#              Information and Software Systems Research               #
14#                            AT&T Research                             #
15#                           Florham Park NJ                            #
16#                                                                      #
17#                  David Korn <dgk@research.att.com>                   #
18#                                                                      #
19########################################################################
20function err_exit
21{
22	print -u$Error_fd -n "\t"
23	print -u$Error_fd -r ${Command}[$1]: "${@:2}"
24	(( Errors+=1 ))
25}
26alias err_exit='err_exit $LINENO'
27
28Command=${0##*/}
29integer Errors=0 Error_fd=2
30
31tmp=$(mktemp -dt) || { err_exit mktemp -dt failed; exit 1; }
32trap "cd /; rm -rf $tmp" EXIT
33
34builtin getconf
35bincat=$(PATH=$(getconf PATH) whence -p cat)
36
37z=()
38z.foo=( [one]=hello [two]=(x=3 y=4) [three]=hi)
39z.bar[0]=hello
40z.bar[2]=world
41z.bar[1]=(x=4 y=5)
42val='(
43	typeset -a bar=(
44		[0]=hello
45		[2]=world
46		[1]=(
47			x=4
48			y=5
49		)
50	)
51	typeset -A foo=(
52		[one]=hello
53		[three]=hi
54		[two]=(
55			x=3
56			y=4
57		)
58	)
59)'
60[[ $z == "$val" ]] || err_exit 'compound variable with mixed arrays not working'
61z.bar[1]=yesyes
62[[ ${z.bar[1]} == yesyes ]] || err_exit 'reassign of index array compound variable fails'
63z.bar[1]=(x=12 y=5)
64[[ ${z.bar[1]} == $'(\n\tx=12\n\ty=5\n)' ]] || err_exit 'reassign array simple to compound variable fails'
65eval val="$z"
66(
67	z.foo[three]=good
68	[[ ${z.foo[three]} == good ]] || err_exit 'associative array assignment in subshell not working'
69)
70[[ $z == "$val" ]] || err_exit 'compound variable changes after associative array assignment'
71eval val="$z"
72(
73	z.foo[two]=ok
74	[[ ${z.foo[two]} == ok ]] || err_exit 'associative array assignment to compound variable in subshell not working'
75	z.bar[1]=yes
76	[[ ${z.bar[1]} == yes ]] || err_exit 'index array assignment to compound variable in subshell not working'
77)
78[[ $z == "$val" ]] || err_exit 'compound variable changes after associative array assignment'
79
80x=(
81	foo=( qqq=abc rrr=def)
82	bar=( zzz=no rst=fed)
83)
84eval val="$x"
85(
86	unset x.foo
87	[[ ${x.foo.qqq} ]] && err_exit 'x.foo.qqq should be unset'
88	x.foo=good
89	[[ ${x.foo} == good ]] || err_exit 'x.foo should be good'
90)
91[[ $x == "$val" ]] || err_exit 'compound variable changes after unset leaves'
92unset l
93(
94	l=( a=1 b="BE" )
95)
96[[ ${l+foo} != foo ]] || err_exit 'l should be unset'
97
98Error_fd=9
99eval "exec $Error_fd>&2 2>/dev/null"
100
101TEST_notfound=notfound
102while	whence $TEST_notfound >/dev/null 2>&1
103do	TEST_notfound=notfound-$RANDOM
104done
105
106
107integer BS=1024 nb=64 ss=60 bs no
108for bs in $BS 1
109do	$SHELL -c '
110		{
111			sleep '$ss'
112			kill -KILL $$
113		} &
114		set -- $(printf %.'$(($BS*$nb))'c x | dd bs='$bs')
115		print ${#1}
116		kill $!
117	' > $tmp/sub 2>/dev/null
118	no=$(<$tmp/sub)
119	(( no == (BS * nb) )) || err_exit "shell hangs on command substitution output size >= $BS*$nb with write size $bs -- expected $((BS*nb)), got ${no:-0}"
120done
121# this time with redirection on the trailing command
122for bs in $BS 1
123do	$SHELL -c '
124		{
125			sleep 2
126			sleep '$ss'
127			kill -KILL $$
128		} &
129		set -- $(printf %.'$(($BS*$nb))'c x | dd bs='$bs' 2>/dev/null)
130		print ${#1}
131		kill $!
132	' > $tmp/sub 2>/dev/null
133	no=$(<$tmp/sub)
134	(( no == (BS * nb) )) || err_exit "shell hangs on command substitution output size >= $BS*$nb with write size $bs and trailing redirection -- expected $((BS*nb)), got ${no:-0}"
135done
136
137# exercise command substitutuion trailing newline logic w.r.t. pipe vs. tmp file io
138
139set -- \
140	'post-line print'								\
141	'$TEST_unset; ($TEST_fork; print 1); print'					\
142	1										\
143	'pre-line print'								\
144	'$TEST_unset; ($TEST_fork; print); print 1'					\
145	$'\n1'										\
146	'multiple pre-line print'							\
147	'$TEST_unset; ($TEST_fork; print); print; ($TEST_fork; print 1); print'		\
148	$'\n\n1'									\
149	'multiple post-line print'							\
150	'$TEST_unset; ($TEST_fork; print 1); print; ($TEST_fork; print); print'		\
151	1										\
152	'intermediate print'								\
153	'$TEST_unset; ($TEST_fork; print 1); print; ($TEST_fork; print 2); print'	\
154	$'1\n\n2'									\
155	'simple variable'								\
156	'$TEST_unset; ($TEST_fork; l=2; print "$l"); print $l'				\
157	2										\
158	'compound variable'								\
159	'$TEST_unset; ($TEST_fork; l=(a=2 b="BE"); print "$l"); print $l'		\
160	$'(\n\ta=2\n\tb=BE\n)'								\
161
162export TEST_fork TEST_unset
163
164while	(( $# >= 3 ))
165do	txt=$1
166	cmd=$2
167	exp=$3
168	shift 3
169	for TEST_unset in '' 'unset var'
170	do	for TEST_fork in '' 'ulimit -c 0'
171		do	for TEST_shell in "eval" "$SHELL -c"
172			do	if	! got=$($TEST_shell "$cmd")
173				then	err_exit "${TEST_shell/*-c/\$SHELL -c} ${TEST_unset:+unset }${TEST_fork:+fork }$txt print failed"
174				elif	[[ "$got" != "$exp" ]]
175				then	EXP=$(printf %q "$exp")
176					GOT=$(printf %q "$got")
177					err_exit "${TEST_shell/*-c/\$SHELL -c} ${TEST_unset:+unset }${TEST_fork:+fork }$txt command substitution failed -- expected $EXP, got $GOT"
178				fi
179			done
180		done
181	done
182done
183
184r=$( ($SHELL -c '
185	{
186		sleep 32
187		kill -KILL $$
188	} &
189	for v in $(set | sed "s/=.*//")
190	do	command unset $v
191	done
192	typeset -Z5 I
193	for ((I = 0; I < 1024; I++))
194	do	eval A$I=1234567890
195	done
196	a=$(set 2>&1)
197	print ok
198	kill -KILL $!
199') 2>/dev/null)
200[[ $r == ok ]] || err_exit "large subshell command substitution hangs"
201
202for TEST_command in '' $TEST_notfound
203do	for TEST_exec in '' 'exec'
204	do	for TEST_fork in '' 'ulimit -c 0;'
205		do	for TEST_redirect in '' '>/dev/null'
206			do	for TEST_substitute in '' ': $'
207				do
208
209	TEST_test="$TEST_substitute($TEST_fork $TEST_exec $TEST_command $TEST_redirect)"
210	[[ $TEST_test == '('*([[:space:]])')' ]] && continue
211	r=$($SHELL -c '
212		{
213			sleep 2
214			kill -KILL $$
215		} &
216		'"$TEST_test"'
217		kill $!
218		print ok
219		')
220	[[ $r == ok ]] || err_exit "shell hangs on $TEST_test"
221
222				done
223			done
224		done
225	done
226done
227
228$SHELL -c '( autoload xxxxx);print -n' ||  err_exit 'autoloaded functions in subshells can cause failure'
229foo=$($SHELL  <<- ++EOF++
230	(trap 'print bar' EXIT;print -n foo)
231	++EOF++
232)
233[[ $foo == foobar ]] || err_exit 'trap on exit when last commands is subshell is not triggered'
234
235err=$(
236	$SHELL  2>&1  <<- \EOF
237	        date=$(whence -p date)
238	        function foo
239	        {
240	                x=$( $date > /dev/null 2>&1 ;:)
241	        }
242		# consume almost all fds to push the test to the fd limit #
243		integer max=$(ulimit --nofile)
244		(( max -= 6 ))
245		for ((i=20; i < max; i++))
246		do	exec {i}>&1
247		done
248	        for ((i=0; i < 20; i++))
249	        do      y=$(foo)
250	        done
251	EOF
252) || {
253	err=${err%%$'\n'*}
254	err=${err#*:}
255	err=${err##[[:space:]]}
256	err_exit "nested command substitution with redirections failed -- $err"
257}
258
259exp=0
260$SHELL -c $'
261	function foobar
262	{
263		print "hello world"
264	}
265	[[ $(getopts \'[+?X\ffoobar\fX]\' v --man 2>&1) == *"Xhello worldX"* ]]
266	exit '$exp$'
267'
268got=$?
269[[ $got == $exp ]] || err_exit "getopts --man runtime callout with nonzero exit terminates shell -- expected '$exp', got '$got'"
270exp=ok
271got=$($SHELL -c $'
272	function foobar
273	{
274		print "hello world"
275	}
276	[[ $(getopts \'[+?X\ffoobar\fX]\' v --man 2>&1) == *"Xhello worldX"* ]]
277	print '$exp$'
278')
279[[ $got == $exp ]] || err_exit "getopts --man runtime callout with nonzero exit terminates shell -- expected '$exp', got '$got'"
280
281# command substitution variations #
282set -- \
283	'$('			')'		\
284	'${ '			'; }'		\
285	'$(ulimit -c 0; '	')'		\
286	'$( ('			') )'		\
287	'${ ('			'); }'		\
288	'`'			'`'		\
289	'`('			')`'		\
290	'`ulimit -c 0; '	'`'		\
291	# end of table #
292exp=ok
293testcase[1]='
294	if	%sexpr "NOMATCH" : ".*Z" >/dev/null%s
295	then	print error
296	else	print ok
297	fi
298	exit %s
299'
300testcase[2]='
301	function bar
302	{
303		pipeout=%1$sprintf Ok | tr O o%2$s
304		print $pipeout
305		return 0
306	}
307	foo=%1$sbar%2$s || foo="exit status $?"
308	print $foo
309	exit %3$s
310'
311while	(( $# >= 2 ))
312do	for ((TEST=1; TEST<=${#testcase[@]}; TEST++))
313	do	body=${testcase[TEST]}
314		for code in 0 2
315		do	got=${ printf "$body" "$1" "$2" "$code" | $SHELL 2>&1 }
316			status=$?
317			if	(( status != code ))
318			then	err_exit "test $TEST '$1...$2 exit $code' failed -- exit status $status, expected $code"
319			elif	[[ $got != $exp ]]
320			then	err_exit "test $TEST '$1...$2 exit $code' failed -- got '$got', expected '$exp'"
321			fi
322		done
323	done
324	shift 2
325done
326
327# the next tests loop on all combinations of
328#	{ SUB CAT INS TST APP } X { file-sizes }
329# where the file size starts at 1Ki and doubles up to and including 1Mi
330#
331# the tests and timeouts are done in async subshells to prevent
332# the test harness from hanging
333
334SUB=(
335	( BEG='$( '	END=' )'	)
336	( BEG='${ '	END='; }'	)
337)
338CAT=(  cat  $bincat  )
339INS=(  ""  "builtin cat; "  "builtin -d cat $bincat; "  ": > /dev/null; "  )
340APP=(  ""  "; :"  )
341TST=(
342	( CMD='print foo | $cat'			EXP=3		)
343	( CMD='$cat < $tmp/lin'						)
344	( CMD='cat $tmp/lin | $cat'					)
345	( CMD='read v < $tmp/buf; print $v'		LIM=4*1024	)
346	( CMD='cat $tmp/buf | read v; print $v'		LIM=4*1024	)
347)
348
349if	cat /dev/fd/3 3</dev/null >/dev/null 2>&1 || whence mkfifo > /dev/null
350then	T=${#TST[@]}
351	TST[T].CMD='$cat <(print foo)'
352	TST[T].EXP=3
353fi
354
355# prime the two data files to 512 bytes each
356# $tmp/lin has newlines every 16 bytes and $tmp/buf has no newlines
357# the outer loop doubles the file size at top
358
359buf=$'1234567890abcdef'
360lin=$'\n1234567890abcde'
361for ((i=0; i<5; i++))
362do	buf=$buf$buf
363	lin=$lin$lin
364done
365print -n "$buf" > $tmp/buf
366print -n "$lin" > $tmp/lin
367
368unset SKIP
369for ((n=1024; n<=1024*1024; n*=2))
370do	cat $tmp/buf $tmp/buf > $tmp/tmp
371	mv $tmp/tmp $tmp/buf
372	cat $tmp/lin $tmp/lin > $tmp/tmp
373	mv $tmp/tmp $tmp/lin
374	for ((S=0; S<${#SUB[@]}; S++))
375	do	for ((C=0; C<${#CAT[@]}; C++))
376		do	cat=${CAT[C]}
377			for ((I=0; I<${#INS[@]}; I++))
378			do	for ((A=0; A<${#APP[@]}; A++))
379				do	for ((T=0; T<${#TST[@]}; T++))
380					do	#undent...#
381
382	if	[[ ! ${SKIP[S][C][I][A][T]} ]]
383	then	eval "{ x=${SUB[S].BEG}${INS[I]}${TST[T].CMD}${APP[A]}${SUB[S].END}; print \${#x}; } >\$tmp/out &"
384		m=$!
385		{ sleep 4; kill -9 $m; } &
386		k=$!
387		wait $m
388		h=$?
389		kill -9 $k
390		wait $k
391		got=$(<$tmp/out)
392		if	[[ ! $got ]] && (( h ))
393		then	got=HUNG
394		fi
395		if	[[ ${TST[T].EXP} ]]
396		then	exp=${TST[T].EXP}
397		else	exp=$n
398		fi
399		if	[[ $got != $exp ]]
400		then	# on failure skip similar tests on larger files sizes #
401			SKIP[S][C][I][A][T]=1
402			siz=$(printf $'%#i' $exp)
403			cmd=${TST[T].CMD//\$cat/$cat}
404			cmd=${cmd//\$tmp\/buf/$siz.buf}
405			cmd=${cmd//\$tmp\/lin/$siz.lin}
406			err_exit "'x=${SUB[S].BEG}${INS[I]}${cmd}${APP[A]}${SUB[S].END} && print \${#x}' failed -- expected '$exp', got '$got'"
407		elif	[[ ${TST[T].EXP} ]] || (( TST[T].LIM >= n ))
408		then	SKIP[S][C][I][A][T]=1
409		fi
410	fi
411
412						#...indent#
413					done
414				done
415			done
416		done
417	done
418done
419
420# specifics -- there's more?
421
422{
423	cmd='{ exec 5>/dev/null; print "$(eval ls -d . 2>&1 1>&5)"; } >$tmp/out &'
424	eval $cmd
425	m=$!
426	{ sleep 4; kill -9 $m; } &
427	k=$!
428	wait $m
429	h=$?
430	kill -9 $k
431	wait $k
432	got=$(<$tmp/out)
433} 2>/dev/null
434exp=''
435if	[[ ! $got ]] && (( h ))
436then	got=HUNG
437fi
438if	[[ $got != $exp ]]
439then	err_exit "eval '$cmd' failed -- expected '$exp', got '$got'"
440fi
441
442float t1=$SECONDS
443sleep=$(whence -p sleep)
444if	[[ $sleep ]]
445then
446	$SHELL -c "( $sleep 5 </dev/null >/dev/null 2>&1 & );exit 0" | cat
447	(( (SECONDS-t1) > 4 )) && err_exit '/bin/sleep& in subshell hanging'
448	((t1=SECONDS))
449fi
450$SHELL -c '( sleep 5 </dev/null >/dev/null 2>&1 & );exit 0' | cat
451(( (SECONDS-t1) > 4 )) && err_exit 'sleep& in subshell hanging'
452
453exp=HOME=$HOME
454( HOME=/bin/sh )
455got=$(env | grep ^HOME=)
456[[ $got == "$exp" ]] ||  err_exit "( HOME=/bin/sh ) cleanup failed -- expected '$exp', got '$got'"
457
458cmd='echo $((case x in x)echo ok;esac);:)'
459exp=ok
460got=$($SHELL -c "$cmd" 2>&1)
461[[ $got == "$exp" ]] ||  err_exit "'$cmd' failed -- expected '$exp', got '$got'"
462
463cmd='eval "for i in 1 2; do eval /bin/echo x; done"'
464exp=$'x\nx'
465got=$($SHELL -c "$cmd")
466if	[[ $got != "$exp" ]]
467then	EXP=$(printf %q "$exp")
468	GOT=$(printf %q "$got")
469	err_exit "'$cmd' failed -- expected $EXP, got $GOT"
470fi
471
472(
473$SHELL -c 'sleep 20 & pid=$!; { x=$( ( seq 60000 ) );kill -9 $pid;}&;wait $pid'
474) 2> /dev/null
475(( $? )) ||  err_exit 'nested command substitution with large output hangs'
476
477(.sh.foo=foobar)
478[[ ${.sh.foo} == foobar ]] && err_exit '.sh subvariables in subshells remain set'
479[[ $($SHELL -c 'print 1 | : "$(/bin/cat <(/bin/cat))"') ]] && err_exit 'process substitution not working correctly in subshells'
480
481# config hang bug
482integer i
483for ((i=1; i < 1000; i++))
484do	typeset foo$i=$i
485done
486{
487    : $( (ac_space=' '; set | grep ac_space) 2>&1)
488} < /dev/null | cat > /dev/null &
489sleep  1.5
490if	kill -KILL $! 2> /dev/null
491then	err_exit 'process timed out with hung comsub'
492fi
493wait $! 2> /dev/null
494(( $? > 128 )) && err_exit 'incorrect exit status with comsub'
495
496$SHELL 2> /dev/null -c '[[ ${ print foo },${ print bar } == foo,bar ]]' || err_exit  '${ print foo },${ print bar } not working'
497$SHELL 2> /dev/null -c '[[ ${ print foo; },${ print bar } == foo,bar ]]' || err_exit  '${ print foo; },${ print bar } not working'
498
499src=$'true 2>&1\n: $(true | true)\n: $(true | true)\n: $(true | true)\n'$(whence -p true)
500exp=ok
501got=$( $SHELL -c "(eval '$src'); echo $exp" )
502[[ $got == "$exp" ]] || err_exit 'subshell eval of pipeline clobbers stdout'
503
504x=$( { time $SHELL -c date >| /dev/null;} 2>&1)
505[[ $x == *real*user*sys* ]] || err_exit 'time { ...;} 2>&1 in $(...) fails'
506
507x=$($SHELL -c '( function fx { export X=123;  } ; fx; ); echo $X')
508[[ $x == 123 ]] && err_exit 'global variables set from with functions inside a
509subshell can leave side effects in parent shell'
510
511date=$(whence -p date)
512err() { return $1; }
513( err 12 ) & pid=$!
514: $( $date)
515wait $pid
516[[ $? == 12 ]] || err_exit 'exit status from subshells not being preserved'
517
518if	cat /dev/fd/3 3</dev/null >/dev/null 2>&1 || whence mkfifo > /dev/null
519then	x="$(sed 's/^/Hello /' <(print "Fred" | sort))"
520	[[ $x == 'Hello Fred' ]] || err_exit  "process substitution of pipeline in command substitution not working"
521fi
522
523{
524$SHELL <<- \EOF
525	function foo
526	{
527		integer i
528		print -u2 foobar
529		for	((i=0; i < 8000; i++))
530		do	print abcdefghijk
531		done
532		print -u2 done
533	}
534	out=$(eval "foo | cat" 2>&1)
535	(( ${#out} == 96011 )) || err_exit "\${#out} is ${#out} should be 96011"
536EOF
537} & pid=$!
538$SHELL -c "{ sleep 4 && kill $pid ;}" 2> /dev/null
539(( $? == 0 )) &&  err_exit 'process has hung'
540
541{
542x=$( $SHELL  <<- \EOF
543	function func1 { typeset IFS; : $(func2); print END ;}
544	function func2 { IFS="BAR"; }
545	func1
546	func1
547EOF
548)
549} 2> /dev/null
550[[ $x == $'END\nEND' ]] || err_exit 'bug in save/restore of IFS in subshell'
551
552true=$(whence -p true)
553date=$(whence -p date)
554tmpf=$tmp/foo
555function fun1
556{
557	$true
558	cd - >/dev/null 2>&1
559	print -u2 -- "$($date) SUCCESS"
560}
561
562print -n $(fun1 2> $tmpf)
563[[  $(< $tmpf) == *SUCCESS ]] || err_exit 'standard error output lost with command substitution'
564
565
566tmpfile=$tmp/foo
567cat > $tmpfile <<-\EOF
568	$SHELL -c 'function g { IFS= ;};function f { typeset IFS;(g);: $V;};f;f'
569	EOF
570$SHELL 2> /dev/null "$tmpfile" || err_exit 'IFS in subshell causes core dump'
571
572unset i
573if      [[ -d /dev/fd ]]
574then    integer i
575        for ((i=11; i < 29; i++))
576        do      if      ! [[ -r /dev/fd/$i  || -w /dev/fd/$i ]]
577                then    a=$($SHELL -c "[[ -r /dev/fd/$i || -w /dev/fd/$i ]]")
578                        (( $? )) || err_exit "file descriptor $i not close on exec"
579                fi
580        done
581fi
582
583trap USR1 USR1
584trap ERR ERR
585[[ $(trap -p USR1) == USR1 ]] || err_exit 'trap -p USR1 in subshell not working'
586[[ $(trap -p ERR) == ERR ]] || err_exit 'trap -p ERR in subshell not working'
587[[ $(trap -p) == *USR* ]] || err_exit 'trap -p in subshell does not contain USR'
588[[ $(trap -p) == *ERR* ]] || err_exit 'trap -p in subshell does not contain ERR'
589trap - USR1 ERR
590
591( PATH=/bin:/usr/bin
592dot=$(cat <<-EOF
593		$(ls -d .)
594	EOF
595) ) & sleep 1
596if      kill -0 $! 2> /dev/null
597then    err_exit  'command substitution containg here-doc with command substitution fails'
598fi
599
600printf=$(whence -p printf)
601[[ $( { trap "echo foobar" EXIT; ( $printf ""); } & wait) == foobar ]] || err_exit  'exit trap not being invoked'
602
603$SHELL 2> /dev/null -c '( PATH=/bin; set -o restricted) ; exit 0'  || err_exit 'restoring PATH when a subshell enables restricted exits not working'
604
605$SHELL <<- \EOF
606	wc=$(whence wc) head=$(whence head)
607	print > /dev/null  $( ( $head -c 1 /dev/zero | ( $wc -c) 3>&1 ) 3>&1) &
608	pid=$!
609	sleep 2
610	kill -9 $! 2> /dev/null && err_exit '/dev/zero in command substitution hangs'
611	wait $!
612EOF
613
614for f in /dev/stdout /dev/fd/1
615do	if	[[ -e $f ]]
616	then	$SHELL -c "x=\$(command -p tee $f </dev/null 2>/dev/null)" || err_exit "$f in command substitution fails"
617	fi
618done
619
620# ========================================
621# Test that closing file descriptors don't affect capturing the output of a
622# subshell. Regression test for issue #198.
623tmpfile=$(mktemp)
624expected='return value'
625
626function get_value {
627 case=$1
628 (( case >= 1 )) && exec 3< $tmpfile
629 (( case >= 2 )) && exec 4< $tmpfile
630 (( case >= 3 )) && exec 6< $tmpfile
631
632 # To trigger the bug we have to spawn an external command. Why is a
633 # mystery but not really relevant.
634 $(whence -p true)
635
636 (( case >= 1 )) && exec 3<&-
637 (( case >= 2 )) && exec 4<&-
638 (( case >= 3 )) && exec 6<&-
639
640 print $expected
641}
642
643actual=$(get_value 0)
644if [[ $actual != $expected ]]
645then
646 err_exit -u2 "failed to capture subshell output when closing fd: case 0"
647fi
648
649actual=$(get_value 1)
650if [[ $actual != $expected ]]
651then
652 err_exit -u2 "failed to capture subshell output when closing fd: case 1"
653fi
654
655actual=$(get_value 2)
656if [[ $actual != $expected ]]
657then
658 err_exit -u2 "failed to capture subshell output when closing fd: case 2"
659fi
660
661actual=$(get_value 3)
662if [[ $actual != $expected ]]
663then
664 err_exit -u2 "failed to capture subshell output when closing fd: case 3"
665fi
666
667rm $tmpfile
668
669exit $((Errors<125?Errors:125))
670