######################################################################## # # # This software is part of the ast package # # Copyright (c) 1982-2009 AT&T Intellectual Property # # and is licensed under the # # Common Public License, Version 1.0 # # by AT&T Intellectual Property # # # # A copy of the License is available at # # http://www.opensource.org/licenses/cpl1.0.txt # # (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) # # # # Information and Software Systems Research # # AT&T Research # # Florham Park NJ # # # # David Korn # # # ######################################################################## function err_exit { print -u2 -n "\t" print -u2 -r ${Command}[$1]: "${@:2}" let Errors+=1 } alias err_exit='err_exit $LINENO' Command=${0##*/} integer Errors=0 tmp=$(mktemp -dt) || { err_exit mktemp -dt failed; exit 1; } trap "cd /; rm -rf $tmp" EXIT # test basic file operations like redirection, pipes, file expansion set -- \ go+r 0000 \ go-r 0044 \ ug=r 0330 \ go+w 0000 \ go-w 0022 \ ug=w 0550 \ go+x 0000 \ go-x 0011 \ ug=x 0660 \ go-rx 0055 \ uo-wx 0303 \ ug-rw 0660 \ o= 0007 while (( $# >= 2 )) do umask 0 umask $1 g=$(umask) [[ $g == $2 ]] || err_exit "umask 0; umask $1 failed -- expected $2, got $g" shift 2 done umask u=rwx,go=rx || err_exit "umask u=rws,go=rx failed" if [[ $(umask -S) != u=rwx,g=rx,o=rx ]] then err_exit 'umask -S incorrect' fi pwd=$PWD [[ $SHELL != /* ]] && SHELL=$pwd/$SHELL cd $tmp || { err_exit "cd $tmp failed"; exit 1; } um=$(umask -S) ( umask 0777; > foobar ) rm -f foobar > foobar [[ -r foobar ]] || err_exit 'umask not being restored after subshell' umask "$um" rm -f foobar # optimizer bug test > foobar for i in 1 2 do print foobar* rm -f foobar done > out if [[ "$( .foo if [[ $(print *) != dir ]] then err_exit 'file expansion leading . not working' fi date > dat1 || err_exit "date > dat1 failed" test -r dat1 || err_exit "dat1 is not readable" x=dat1 cat <$x > dat2 || err_exit "cat < $x > dat2 failed" cat dat1 dat2 | cat | cat | cat > dat3 || err_exit "cat pipe failed" cat > dat4 < dat5 & wait $! set -- dat* if (( $# != 5 )) then err_exit "dat* matches only $# files" fi if (command > foo\\abc) 2> /dev/null then set -- foo* if [[ $1 != 'foo\abc' ]] then err_exit 'foo* does not match foo\abc' fi fi if ( : > TT* && : > TTfoo ) 2>/dev/null then set -- TT* if (( $# < 2 )) then err_exit 'TT* not expanding when file TT* exists' fi fi cd ~- || err_exit "cd back failed" cat > $tmp/script <<- ! #! $SHELL print -r -- \$0 ! chmod 755 $tmp/script if [[ $($tmp/script) != "$tmp/script" ]] then err_exit '$0 not correct for #! script' fi bar=foo eval foo=\$bar if [[ $foo != foo ]] then err_exit 'eval foo=\$bar not working' fi bar='foo=foo\ bar' eval $bar if [[ $foo != 'foo bar' ]] then err_exit 'eval foo=\$bar, with bar="foo\ bar" not working' fi cd /tmp cd ../../tmp || err_exit "cd ../../tmp failed" if [[ $PWD != /tmp ]] then err_exit 'cd ../../tmp is not /tmp' fi ( sleep 2; cat < $tmp/foobar & wait $! foobar=$( < $tmp/foobar) if [[ $foobar != foobar ]] then err_exit "$foobar is not foobar" fi { print foo /bin/echo bar print bam } > $tmp/foobar if [[ $( < $tmp/foobar) != $'foo\nbar\nbam' ]] then err_exit "output file pointer not shared correctly" fi cat > $tmp/foobar <<\! print foo /bin/echo bar print bam ! chmod +x $tmp/foobar if [[ $($tmp/foobar) != $'foo\nbar\nbam' ]] then err_exit "script not working" fi if [[ $($tmp/foobar | /bin/cat) != $'foo\nbar\nbam' ]] then err_exit "script | cat not working" fi if [[ $( $tmp/foobar) != $'foo\nbar\nbam' ]] then err_exit "output file pointer not shared correctly" fi rm -f $tmp/foobar x=$( (print foo) ; (print bar) ) if [[ $x != $'foo\nbar' ]] then err_exit " ( (print foo);(print bar ) failed" fi x=$( (/bin/echo foo) ; (print bar) ) if [[ $x != $'foo\nbar' ]] then err_exit " ( (/bin/echo);(print bar ) failed" fi x=$( (/bin/echo foo) ; (/bin/echo bar) ) if [[ $x != $'foo\nbar' ]] then err_exit " ( (/bin/echo);(/bin/echo bar ) failed" fi cat > $tmp/script <<\! if [[ -p /dev/fd/0 ]] then builtin cat cat - > /dev/null [[ -p /dev/fd/0 ]] && print ok else print no fi ! chmod +x $tmp/script case $( (print) | $tmp/script;:) in ok) ;; no) err_exit "[[ -p /dev/fd/0 ]] fails for standard input pipe" ;; *) err_exit "builtin replaces standard input pipe" ;; esac print 'print $0' > $tmp/script print ". $tmp/script" > $tmp/scriptx chmod +x $tmp/scriptx if [[ $($tmp/scriptx) != $tmp/scriptx ]] then err_exit '$0 not correct for . script' fi cd $tmp || { err_exit "cd $tmp failed"; exit 1; } print ./b > ./a; print ./c > b; print ./d > c; print ./e > d; print "echo \"hello there\"" > e chmod 755 a b c d e x=$(./a) if [[ $x != "hello there" ]] then err_exit "nested scripts failed" fi x=$( (./a) | cat) if [[ $x != "hello there" ]] then err_exit "scripts in subshells fail" fi cd ~- || err_exit "cd back failed" x=$( (/bin/echo foo) 2> /dev/null ) if [[ $x != foo ]] then err_exit "subshell in command substitution fails" fi exec 1>&- x=$(print hello) if [[ $x != hello ]] then err_exit "command subsitution with stdout closed failed" fi cd $pwd x=$(cat <<\! | $SHELL /bin/echo | /bin/cat /bin/echo hello ! ) if [[ $x != $'\n'hello ]] then err_exit "$SHELL not working when standard input is a pipe" fi x=$( (/bin/echo hello) 2> /dev/null ) if [[ $x != hello ]] then err_exit "subshell in command substitution with 1 closed fails" fi cat > $tmp/script <<- \! read line 2> /dev/null print done ! if [[ $($SHELL $tmp/script <&-) != done ]] then err_exit "executing script with 0 closed fails" fi trap '' INT cat > $tmp/script <<- \! trap 'print bad' INT kill -s INT $$ print good ! chmod +x $tmp/script if [[ $($SHELL $tmp/script) != good ]] then err_exit "traps ignored by parent not ignored" fi trap - INT cat > $tmp/script <<- \! read line /bin/cat ! if [[ $($SHELL $tmp/script < /dev/null ) == $'foobar\nfoobar' ]] || err_exit 'for loop optimization subshell bug' command exec 3<> /dev/null if cat /dev/fd/3 >/dev/null 2>&1 then [[ $($SHELL -c 'cat <(print foo)' 2> /dev/null) == foo ]] || err_exit 'process substitution not working' [[ $($SHELL -c $'tee >(grep \'1$\' > '$tmp/scriptx$') > /dev/null <<- \!!! line0 line1 line2 !!! wait cat '$tmp/scriptx 2> /dev/null) == line1 ]] || err_exit '>() process substitution fails' > $tmp/scriptx [[ $($SHELL -c $' for i in 1 do tee >(grep \'1$\' > '$tmp/scriptx$') > /dev/null <<- \!!! line0 line1 line2 !!! done wait cat '$tmp/scriptx 2>> /dev/null) == line1 ]] || err_exit '>() process substitution fails in for loop' [[ $({ $SHELL -c 'cat <(for i in x y z; do print $i; done)';} 2> /dev/null) == $'x\ny\nz' ]] || err_exit 'process substitution of compound commands not working' fi [[ $($SHELL -r 'command -p :' 2>&1) == *restricted* ]] || err_exit 'command -p not restricted' print cat > $tmp/scriptx chmod +x $tmp/scriptx [[ $($SHELL -c "print foo | $tmp/scriptx ;:" 2> /dev/null ) == foo ]] || err_exit 'piping into script fails' [[ $($SHELL -c 'X=1;print -r -- ${X:=$(expr "a(0)" : '"'a*(\([^)]\))')}'" 2> /dev/null) == 1 ]] || err_exit 'x=1;${x:=$(..."...")} failure' [[ $($SHELL -c 'print -r -- ${X:=$(expr "a(0)" : '"'a*(\([^)]\))')}'" 2> /dev/null) == 0 ]] || err_exit '${x:=$(..."...")} failure' exec 3<&- if [[ -d /dev/fd && -w /dev/fd/3 ]] then [[ $(cat <(print hello) ) == hello ]] || err_exit "process substitution not working outside for or while loop" $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" fi exec 3> /dev/null print 'print foo "$@"' > $tmp/scriptx [[ $( print "($tmp/scriptx bar)" | $SHELL 2>/dev/null) == 'foo bar' ]] || err_exit 'script pipe to shell fails' print "#! $SHELL" > $tmp/scriptx print 'print -- $0' >> $tmp/scriptx chmod +x $tmp/scriptx [[ $($tmp/scriptx) == $tmp/scriptx ]] || err_exit "\$0 is $0 instead of $tmp/scriptx" cat > $tmp/scriptx <<- \EOF myfilter() { x=$(print ok | cat); print -r -- $SECONDS;} set -o pipefail sleep 3 | myfilter EOF (( $($SHELL $tmp/scriptx) > 2.0 )) && err_exit 'command substitution causes pipefail option to hang' exec 3<&- ( typeset -r foo=bar) 2> /dev/null || err_exit 'readonly variables set in a subshell cannot unset' $SHELL -c 'x=${ print hello;}; [[ $x == hello ]]' 2> /dev/null || err_exit '${ command;} not supported' $SHELL 2> /dev/null <<- \EOF || err_exit 'multiline ${...} command substitution not supported' x=${ print hello } [[ $x == hello ]] EOF $SHELL 2> /dev/null <<- \EOF || err_exit '${...} command substitution with side effects not supported ' y=bye x=${ y=hello print hello } [[ $y == $x ]] EOF $SHELL 2> /dev/null <<- \EOF || err_exit 'nested ${...} command substitution not supported' x=${ print ${ print hello;} $(print world) } [[ $x == 'hello world' ]] EOF $SHELL 2> /dev/null <<- \EOF || err_exit 'terminating } is not a reserved word with ${ command }' x=${ { print -n } ; print -n hello ; } ; print ' world' } [[ $x == '}hello world' ]] EOF $SHELL 2> /dev/null <<- \EOF || err_exit '${ command;}xxx not working' f() { print foo } [[ ${ f;}bar == foobar ]] EOF unset foo [[ ! ${foo[@]} ]] || err_exit '${foo[@]} is not empty when foo is unset' [[ ! ${foo[3]} ]] || err_exit '${foo[3]} is not empty when foo is unset' [[ $(print "[${ print foo }]") == '[foo]' ]] || err_exit '${...} not working when } is followed by ]' [[ $(print "${ print "[${ print foo }]" }") == '[foo]' ]] || err_exit 'nested ${...} not working when } is followed by ]' unset foo foo=$(false) > /dev/null && err_exit 'failed command substitution with redirection not returning false' expected=foreback got=$(print -n fore;(sleep 2;print back)&) [[ $got == $expected ]] || err_exit "command substitution background process output error -- got '$got', expected '$expected'" binfalse=$(whence -p false) for false in false $binfalse do x=$($false) && err_exit "x=\$($false) should fail" $($false) && err_exit "\$($false) should fail" $($false) > /dev/null && err_exit "\$($false) > /dev/null should fail" done if env x-a=y >/dev/null 2>&1 then [[ $(env 'x-a=y' $SHELL -c 'env | grep x-a') == *x-a=y* ]] || err_exit 'invalid environment variables not preserved' fi float s=SECONDS sleep=$(whence -p sleep) for i in 1 2 do print $i done | while read sec; do ( $sleep $sec; $sleep $sec) done (( (SECONDS-s) < 4)) && err_exit '"command | while read...done" finishing too fast' s=SECONDS set -o pipefail for ((i=0; i < 30; i++)) do print hello sleep .1 done | $sleep 1 (( (SECONDS-s) < 2 )) || err_exit 'early termination not causing broken pipe' [[ $({ trap 'print trap' 0; print -n | $(whence -p cat); } & wait $!) == trap ]] || err_exit 'trap on exit not getting triggered' var=$({ trap 'print trap' ERR; print -n | $binfalse; } & wait $!) [[ $var == trap ]] || err_exit 'trap on ERR not getting triggered' exp= got=$( function fun { $binfalse && echo FAILED } : works if this line deleted : | fun : works if this line deleted : ) [[ $got == $exp ]] || err_exit "pipe to function with conditional fails -- expected '$exp', got '$got'" got=$( : works if this line deleted : | { $binfalse && echo FAILED; } : works if this line deleted : ) [[ $got == $exp ]] || err_exit "pipe to { ... } with conditional fails -- expected '$exp', got '$got'" got=$( : works if this line deleted : | ( $binfalse && echo FAILED ) : works if this line deleted : ) [[ $got == $exp ]] || err_exit "pipe to ( ... ) with conditional fails -- expected '$exp', got '$got'" ( $SHELL -c 'trap : DEBUG; x=( $foo); exit 0') 2> /dev/null || err_exit 'trap DEBUG fails' exit $((Errors))