xref: /titanic_44/usr/src/lib/libshell/common/tests/basic.sh (revision 1e4c938b57d1656808e4112127ff1dce3eba5314)
1########################################################################
2#                                                                      #
3#               This software is part of the ast package               #
4#          Copyright (c) 1982-2010 AT&T Intellectual Property          #
5#                      and is licensed under the                       #
6#                  Common Public License, Version 1.0                  #
7#                    by AT&T Intellectual Property                     #
8#                                                                      #
9#                A copy of the License is available at                 #
10#            http://www.opensource.org/licenses/cpl1.0.txt             #
11#         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         #
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 -u2 -n "\t"
23	print -u2 -r ${Command}[$1]: "${@:2}"
24	let Errors+=1
25}
26alias err_exit='err_exit $LINENO'
27
28Command=${0##*/}
29integer Errors=0
30
31tmp=$(mktemp -dt) || { err_exit mktemp -dt failed; exit 1; }
32trap "cd /; rm -rf $tmp" EXIT
33
34# test basic file operations like redirection, pipes, file expansion
35set -- \
36	go+r	0000	\
37	go-r	0044	\
38	ug=r	0330	\
39	go+w	0000	\
40	go-w	0022	\
41	ug=w	0550	\
42	go+x	0000	\
43	go-x	0011	\
44	ug=x	0660	\
45	go-rx	0055	\
46	uo-wx	0303	\
47	ug-rw	0660	\
48	o=	0007
49while	(( $# >= 2 ))
50do	umask 0
51	umask $1
52	g=$(umask)
53	[[ $g == $2 ]] || err_exit "umask 0; umask $1 failed -- expected $2, got $g"
54	shift 2
55done
56umask u=rwx,go=rx || err_exit "umask u=rws,go=rx failed"
57if	[[ $(umask -S) != u=rwx,g=rx,o=rx ]]
58then	err_exit 'umask -S incorrect'
59fi
60pwd=$PWD
61[[ $SHELL != /* ]] && SHELL=$pwd/$SHELL
62cd $tmp || { err_exit "cd $tmp failed"; exit 1; }
63um=$(umask -S)
64( umask 0777; > foobar )
65rm -f foobar
66> foobar
67[[ -r foobar ]] || err_exit 'umask not being restored after subshell'
68umask "$um"
69rm -f foobar
70# optimizer bug test
71> foobar
72for i in 1 2
73do      print foobar*
74        rm -f foobar
75done > out
76if      [[ "$(<out)"  != "foobar"$'\n'"foobar*" ]]
77then    print -u2 "optimizer bug with file expansion"
78fi
79rm -f out foobar
80mkdir dir
81if	[[ $(print */) != dir/ ]]
82then	err_exit 'file expansion with trailing / not working'
83fi
84if	[[ $(print *) != dir ]]
85then	err_exit 'file expansion with single file not working'
86fi
87print hi > .foo
88if	[[ $(print *) != dir ]]
89then	err_exit 'file expansion leading . not working'
90fi
91date > dat1 || err_exit "date > dat1 failed"
92test -r dat1 || err_exit "dat1 is not readable"
93x=dat1
94cat <$x > dat2 || err_exit "cat < $x > dat2 failed"
95cat dat1 dat2 | cat  | cat | cat > dat3 || err_exit "cat pipe failed"
96cat > dat4 <<!
97$(date)
98!
99cat dat1 dat2 | cat  | cat | cat > dat5 &
100wait $!
101set -- dat*
102if	(( $# != 5 ))
103then	err_exit "dat* matches only $# files"
104fi
105if	(command > foo\\abc) 2> /dev/null
106then	set -- foo*
107	if	[[ $1 != 'foo\abc' ]]
108	then	err_exit 'foo* does not match foo\abc'
109	fi
110fi
111if ( : > TT* && : > TTfoo ) 2>/dev/null
112then	set -- TT*
113	if	(( $# < 2 ))
114	then	err_exit 'TT* not expanding when file TT* exists'
115	fi
116fi
117cd ~- || err_exit "cd back failed"
118cat > $tmp/script <<- !
119	#! $SHELL
120	print -r -- \$0
121!
122chmod 755 $tmp/script
123if	[[ $($tmp/script) != "$tmp/script" ]]
124then	err_exit '$0 not correct for #! script'
125fi
126bar=foo
127eval foo=\$bar
128if	[[ $foo != foo ]]
129then	err_exit 'eval foo=\$bar not working'
130fi
131bar='foo=foo\ bar'
132eval $bar
133if	[[ $foo != 'foo bar' ]]
134then	err_exit 'eval foo=\$bar, with bar="foo\ bar" not working'
135fi
136cd /tmp
137cd ../../tmp || err_exit "cd ../../tmp failed"
138if	[[ $PWD != /tmp ]]
139then	err_exit 'cd ../../tmp is not /tmp'
140fi
141( sleep 2; cat <<!
142foobar
143!
144) | cat > $tmp/foobar &
145wait $!
146foobar=$( < $tmp/foobar)
147if	[[ $foobar != foobar ]]
148then	err_exit "$foobar is not foobar"
149fi
150{
151	print foo
152	/bin/echo bar
153	print bam
154} > $tmp/foobar
155if	[[ $( < $tmp/foobar) != $'foo\nbar\nbam' ]]
156then	err_exit "output file pointer not shared correctly"
157fi
158cat > $tmp/foobar <<\!
159	print foo
160	/bin/echo bar
161	print bam
162!
163chmod +x $tmp/foobar
164if	[[ $($tmp/foobar) != $'foo\nbar\nbam' ]]
165then	err_exit "script not working"
166fi
167if	[[ $($tmp/foobar | /bin/cat) != $'foo\nbar\nbam' ]]
168then	err_exit "script | cat not working"
169fi
170if	[[ $( $tmp/foobar) != $'foo\nbar\nbam' ]]
171then	err_exit "output file pointer not shared correctly"
172fi
173rm -f $tmp/foobar
174x=$( (print foo) ; (print bar) )
175if	[[ $x != $'foo\nbar' ]]
176then	err_exit " ( (print foo);(print bar ) failed"
177fi
178x=$( (/bin/echo foo) ; (print bar) )
179if	[[ $x != $'foo\nbar' ]]
180then	err_exit " ( (/bin/echo);(print bar ) failed"
181fi
182x=$( (/bin/echo foo) ; (/bin/echo bar) )
183if	[[ $x != $'foo\nbar' ]]
184then	err_exit " ( (/bin/echo);(/bin/echo bar ) failed"
185fi
186cat > $tmp/script <<\!
187if	[[ -p /dev/fd/0 ]]
188then	builtin cat
189	cat - > /dev/null
190	[[ -p /dev/fd/0 ]] && print ok
191else	print no
192fi
193!
194chmod +x $tmp/script
195case $( (print) | $tmp/script;:) in
196ok)	;;
197no)	err_exit "[[ -p /dev/fd/0 ]] fails for standard input pipe" ;;
198*)	err_exit "builtin replaces standard input pipe" ;;
199esac
200print 'print $0' > $tmp/script
201print ". $tmp/script" > $tmp/scriptx
202chmod +x $tmp/scriptx
203if	[[ $($tmp/scriptx) != $tmp/scriptx ]]
204then	err_exit '$0 not correct for . script'
205fi
206cd $tmp || { err_exit "cd $tmp failed"; exit 1; }
207print ./b > ./a; print ./c > b; print ./d > c; print ./e > d; print "echo \"hello there\"" > e
208chmod 755 a b c d e
209x=$(./a)
210if	[[ $x != "hello there" ]]
211then	err_exit "nested scripts failed"
212fi
213x=$( (./a) | cat)
214if	[[ $x != "hello there" ]]
215then	err_exit "scripts in subshells fail"
216fi
217cd ~- || err_exit "cd back failed"
218x=$( (/bin/echo foo) 2> /dev/null )
219if	[[ $x != foo ]]
220then	err_exit "subshell in command substitution fails"
221fi
222exec 1>&-
223x=$(print hello)
224if	[[ $x != hello ]]
225then	err_exit "command subsitution with stdout closed failed"
226fi
227cd $pwd
228x=$(cat <<\! | $SHELL
229/bin/echo | /bin/cat
230/bin/echo hello
231!
232)
233if	[[ $x != $'\n'hello ]]
234then	err_exit "$SHELL not working when standard input is a pipe"
235fi
236x=$( (/bin/echo hello) 2> /dev/null )
237if	[[ $x != hello ]]
238then	err_exit "subshell in command substitution with 1 closed fails"
239fi
240cat > $tmp/script <<- \!
241read line 2> /dev/null
242print done
243!
244if	[[ $($SHELL $tmp/script <&-) != done ]]
245then	err_exit "executing script with 0 closed fails"
246fi
247trap '' INT
248cat > $tmp/script <<- \!
249trap 'print bad' INT
250kill -s INT $$
251print good
252!
253chmod +x $tmp/script
254if	[[ $($SHELL  $tmp/script) != good ]]
255then	err_exit "traps ignored by parent not ignored"
256fi
257trap - INT
258cat > $tmp/script <<- \!
259read line
260/bin/cat
261!
262if	[[ $($SHELL $tmp/script <<!
263one
264two
265!
266)	!= two ]]
267then	err_exit "standard input not positioned correctly"
268fi
269word=$(print $'foo\nbar' | { read line; /bin/cat;})
270if	[[ $word != bar ]]
271then	err_exit "pipe to { read line; /bin/cat;} not working"
272fi
273word=$(print $'foo\nbar' | ( read line; /bin/cat) )
274if	[[ $word != bar ]]
275then	err_exit "pipe to ( read line; /bin/cat) not working"
276fi
277if	[[ $(print x{a,b}y) != 'xay xby' ]]
278then	err_exit 'brace expansion not working'
279fi
280if	[[ $(for i in foo bar
281	  do ( tgz=$(print $i)
282	  print $tgz)
283	  done) != $'foo\nbar' ]]
284then	err_exit 'for loop subshell optimizer bug'
285fi
286unset a1
287optbug()
288{
289	set -A a1  foo bar bam
290	integer i
291	for ((i=0; i < 3; i++))
292	do
293		(( ${#a1[@]} < 2 )) && return 0
294		set -- "${a1[@]}"
295		shift
296		set -A a1 -- "$@"
297	done
298	return 1
299}
300optbug ||  err_exit 'array size optimzation bug'
301wait # not running --pipefail which would interfere with subsequent tests
302: $(jobs -p) # required to clear jobs for next jobs -p (interactive side effect)
303sleep 20 &
304pids=$!
305if	[[ $(jobs -p) != $! ]]
306then	err_exit 'jobs -p not reporting a background job'
307fi
308sleep 20 &
309pids="$pids $!"
310foo()
311{
312	set -- $(jobs -p)
313	(( $# == 2 )) || err_exit "$# jobs not reported -- 2 expected"
314}
315foo
316kill $pids
317
318[[ $( (trap 'print alarm' ALRM; sleep 4) & sleep 2; kill -ALRM $!; sleep 2; wait) == alarm ]] || err_exit 'ALRM signal not working'
319[[ $($SHELL -c 'trap "" HUP; $SHELL -c "(sleep 2;kill -HUP $$)& sleep 4;print done"') != done ]] && err_exit 'ignored traps not being ignored'
320[[ $($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'
321command exec 3<> /dev/null
322if	cat /dev/fd/3 >/dev/null 2>&1
323then	[[ $($SHELL -c 'cat <(print foo)' 2> /dev/null) == foo ]] || err_exit 'process substitution not working'
324	[[ $($SHELL -c  $'tee >(grep \'1$\' > '$tmp/scriptx$') > /dev/null <<-  \!!!
325	line0
326	line1
327	line2
328	!!!
329	wait
330	cat '$tmp/scriptx 2> /dev/null)  == line1 ]] || err_exit '>() process substitution fails'
331	> $tmp/scriptx
332	[[ $($SHELL -c  $'
333	for i in 1
334	do	tee >(grep \'1$\' > '$tmp/scriptx$') > /dev/null  <<-  \!!!
335		line0
336		line1
337		line2
338		!!!
339	done
340	wait
341	cat '$tmp/scriptx 2>> /dev/null) == line1 ]] || err_exit '>() process substitution fails in for loop'
342	[[ $({ $SHELL -c 'cat <(for i in x y z; do print $i; done)';} 2> /dev/null) == $'x\ny\nz' ]] ||
343		err_exit 'process substitution of compound commands not working'
344fi
345[[ $($SHELL -r 'command -p :' 2>&1) == *restricted* ]]  || err_exit 'command -p not restricted'
346print cat >  $tmp/scriptx
347chmod +x $tmp/scriptx
348[[ $($SHELL -c "print foo | $tmp/scriptx ;:" 2> /dev/null ) == foo ]] || err_exit 'piping into script fails'
349[[ $($SHELL -c 'X=1;print -r -- ${X:=$(expr "a(0)" : '"'a*(\([^)]\))')}'" 2> /dev/null) == 1 ]] || err_exit 'x=1;${x:=$(..."...")} failure'
350[[ $($SHELL -c 'print -r -- ${X:=$(expr "a(0)" : '"'a*(\([^)]\))')}'" 2> /dev/null) == 0 ]] || err_exit '${x:=$(..."...")} failure'
351exec 3<&-
352if	[[ -d /dev/fd  && -w /dev/fd/3 ]]
353then	[[ $(cat <(print hello) ) == hello ]] || err_exit "process substitution not working outside for or while loop"
354	$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"
355fi
356exec 3> /dev/null
357print 'print foo "$@"' > $tmp/scriptx
358[[ $( print "($tmp/scriptx bar)" | $SHELL 2>/dev/null) == 'foo bar' ]] || err_exit 'script pipe to shell fails'
359print "#! $SHELL" > $tmp/scriptx
360print 'print  -- $0' >> $tmp/scriptx
361chmod +x $tmp/scriptx
362[[ $($tmp/scriptx) == $tmp/scriptx ]] || err_exit  "\$0 is $0 instead of $tmp/scriptx"
363cat > $tmp/scriptx <<- \EOF
364	myfilter() { x=$(print ok | cat); print  -r -- $SECONDS;}
365	set -o pipefail
366	sleep 3 | myfilter
367EOF
368(( $($SHELL $tmp/scriptx) > 2.0 )) && err_exit 'command substitution causes pipefail option to hang'
369exec 3<&-
370( typeset -r foo=bar) 2> /dev/null || err_exit 'readonly variables set in a subshell cannot unset'
371$SHELL -c 'x=${ print hello;}; [[ $x == hello ]]' 2> /dev/null || err_exit '${ command;} not supported'
372$SHELL 2> /dev/null <<- \EOF || err_exit 'multiline ${...} command substitution not supported'
373	x=${
374		print hello
375	}
376	[[ $x == hello ]]
377EOF
378$SHELL 2> /dev/null <<- \EOF || err_exit '${...} command substitution with side effects not supported '
379	y=bye
380	x=${
381		y=hello
382		print hello
383	}
384	[[ $y == $x ]]
385EOF
386$SHELL   2> /dev/null <<- \EOF || err_exit 'nested ${...} command substitution not supported'
387	x=${
388		print ${ print hello;} $(print world)
389	}
390	[[ $x == 'hello world' ]]
391EOF
392$SHELL   2> /dev/null <<- \EOF || err_exit 'terminating } is not a reserved word with ${ command }'
393	x=${	{ print -n } ; print -n hello ; }  ; print ' world' }
394	[[ $x == '}hello world' ]]
395EOF
396$SHELL   2> /dev/null <<- \EOF || err_exit '${ command;}xxx not working'
397	f()
398	{
399		print foo
400	}
401	[[ ${ f;}bar == foobar ]]
402EOF
403
404unset foo
405[[ ! ${foo[@]} ]] || err_exit '${foo[@]} is not empty when foo is unset'
406[[ ! ${foo[3]} ]] || err_exit '${foo[3]} is not empty when foo is unset'
407[[ $(print  "[${ print foo }]") == '[foo]' ]] || err_exit '${...} not working when } is followed by ]'
408[[ $(print  "${ print "[${ print foo }]" }") == '[foo]' ]] || err_exit 'nested ${...} not working when } is followed by ]'
409unset foo
410foo=$(false) > /dev/null && err_exit 'failed command substitution with redirection not returning false'
411expected=foreback
412got=$(print -n fore;(sleep 2;print back)&)
413[[ $got == $expected ]] || err_exit "command substitution background process output error -- got '$got', expected '$expected'"
414
415binfalse=$(whence -p false)
416for false in false $binfalse
417do	x=$($false) && err_exit "x=\$($false) should fail"
418	$($false) && err_exit "\$($false) should fail"
419	$($false) > /dev/null && err_exit "\$($false) > /dev/null should fail"
420done
421if	env x-a=y >/dev/null 2>&1
422then	[[ $(env 'x-a=y'  $SHELL -c 'env | grep x-a') == *x-a=y* ]] || err_exit 'invalid environment variables not preserved'
423fi
424float s=SECONDS
425sleep=$(whence -p sleep)
426for i in 1 2
427do      print $i
428done | while read sec; do ( $sleep $sec; $sleep $sec) done
429(( (SECONDS-s)  < 4)) && err_exit '"command | while read...done" finishing too fast'
430s=SECONDS
431set -o pipefail
432for ((i=0; i < 30; i++))
433do	print hello
434	sleep .1
435done |  $sleep 1
436(( (SECONDS-s) < 2 )) || err_exit 'early termination not causing broken pipe'
437[[ $({ trap 'print trap' 0; print -n | $(whence -p cat); } & wait $!) == trap ]] || err_exit 'trap on exit not getting triggered'
438var=$({ trap 'print trap' ERR; print -n | $binfalse; } & wait $!)
439[[ $var == trap ]] || err_exit 'trap on ERR not getting triggered'
440
441exp=
442got=$(
443	function fun
444	{
445		$binfalse && echo FAILED
446	}
447	: works if this line deleted : |
448	fun
449	: works if this line deleted :
450)
451[[ $got == $exp ]] || err_exit "pipe to function with conditional fails -- expected '$exp', got '$got'"
452got=$(
453	: works if this line deleted : |
454	{ $binfalse && echo FAILED; }
455	: works if this line deleted :
456)
457[[ $got == $exp ]] || err_exit "pipe to { ... } with conditional fails -- expected '$exp', got '$got'"
458
459got=$(
460	: works if this line deleted : |
461	( $binfalse && echo FAILED )
462	: works if this line deleted :
463)
464[[ $got == $exp ]] || err_exit "pipe to ( ... ) with conditional fails -- expected '$exp', got '$got'"
465
466( $SHELL -c 'trap : DEBUG; x=( $foo); exit 0') 2> /dev/null  || err_exit 'trap DEBUG fails'
467
468true=$(whence -p true)
469set -o pipefail
470float start=$SECONDS end
471for ((i=0; i < 2; i++))
472do	print foo
473	sleep 1.5
474done | { read; $true; end=$SECONDS ;}
475(( (SECONDS-start) < 1 )) && err_exit "pipefail not waiting for pipe to finish"
476set +o pipefail
477(( (SECONDS-start) > 2 )) &&  err_exit "pipefail causing /bin/true to wait for other end of pipe"
478
479
480{ env A__z=C+SHLVL $SHELL -c : ;} 2> /dev/null || err_exit "SHLVL with wrong attribute fails"
481
482exit $((Errors))
483