1#! test/for/moderni/sh 2# See the file LICENSE in the main modernish directory for the licence. 3 4# Regression tests related to file descriptors, redirection, pipelines, and other I/O matters. 5 6TEST title='blocks can save a closed file descriptor' 7 # zsh-5.0.7 displays an error when trying to close an already-closed file 8 # descriptor, but the exit status is still 0, so catch stderr output. 9 v=$(set +x; exec 2>&1; { :; } 4>&-) 10 str empty $v || return 1 11 # Now check for correct BUG_SCLOSEDFD detection 12 { 13 { 14 while :; do 15 { 16 exec 4>/dev/null 17 } 4>&- 18 break 19 done 4>&- 20 # does the 4>/dev/null leak out of of both a loop and a { ...; } block? 21 if { true >&4; } 2>/dev/null; then 22 mustHave BUG_SCLOSEDFD 23 else 24 mustNotHave BUG_SCLOSEDFD 25 fi 26 } 4>&- 27 } 4>/dev/null # BUG_SCLOSEDFD workaround 28 if eq $? 1 || { true >&4; } 2>/dev/null; then 29 return 1 30 elif isset xfailmsg; then 31 return 2 32 fi 33ENDT 4>&- 34 35TEST title="pipeline commands are run in subshells" 36 # POSIX says at http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_12 37 # "[...] as an extension, however, any or all commands in 38 # a pipeline may be executed in the current environment." 39 # Some shells execute the last element of a pipeline in the current environment (feature ID: 40 # LEPIPEMAIN), but there are no currently existing shells that execute any other element of a 41 # pipeline in the current environment. Scripts may break if a shell ever does. At the very least 42 # it would require another modernish feature ID (e.g. ALLPIPEMAIN). Until then, this sanity check 43 # should fail if that condition is ever detected. 44 v1= v2= v3= v4= 45 # QRK_APIPEMAIN compat: use assignment-arguments, not real assignments 46 # QRK_PPIPEMAIN compat: don't use assignments in parameter substitutions, eg. : ${v1=1} 47 export v1=1 | export v2=2 | export v3=3 | export v4=4 48 v=$v1$v2$v3$v4 49 unset -v v1 v2 v3 v4 50 case $v in 51 ( '' ) mustNotHave LEPIPEMAIN ;; 52 ( 4 ) mustHave LEPIPEMAIN ;; 53 (1234) failmsg="need ALLPIPEMAIN feature ID"; return 1 ;; 54 ( * ) failmsg="need new shell quirk ID ($v1$v2$v3$v4)"; return 1 ;; 55 esac 56ENDT 57 58TEST title='simple assignments in pipeline elements' 59 unset -v v1 v2 60 # LEPIPEMAIN compat: no assignment in last element 61 true | v1=foo | putln "junk" | v2=bar | cat 62 case ${v1-U},${v2-U} in 63 ( U,U ) mustNotHave QRK_APIPEMAIN ;; 64 ( foo,bar ) 65 mustHave QRK_APIPEMAIN ;; 66 ( * ) return 1 ;; 67 esac 68ENDT 69 70TEST title='param substitutions in pipeline elements' 71 unset -v v1 v2 72 # LEPIPEMAIN compat: no param subst in last element 73 true | : ${v1=foo} | putln "junk" | : ${v2=bar} | cat 74 case ${v1-U},${v2-U} in 75 ( U,U ) mustNotHave QRK_PPIPEMAIN ;; 76 ( foo,bar ) 77 mustHave QRK_PPIPEMAIN ;; 78 ( * ) return 1 ;; 79 esac 80ENDT 81 82TEST title="'>>' redirection can create new file" 83 { put '' >>$tempdir/io-test5; } 2>/dev/null && mustNotHave BUG_APPENDC || mustHave BUG_APPENDC 84ENDT 85 86TEST title="I/O redir on func defs honoured in pipes" 87 foo() { 88 putln 'redir-ok' 2>/dev/null >&5 89 putln 'fn-ok' 90 } 5>$tempdir/io-test6 91 # On bash 2.05b and 3.0, the redirection is forgotten only if the function 92 # is piped through a command, so we add '| cat' to fail on this. 93 case $(umask 007; foo | cat) in 94 ( fn-ok ) 95 is reg $tempdir/io-test6 && read v <$tempdir/io-test6 && str eq $v 'redir-ok' || return 1 ;; 96 ( * ) return 1 ;; 97 esac 98ENDT 99 100TEST title='globbing works regardless of IFS' 101 push -o noglob IFS 102 set +o noglob 103 IFS=$ASCIICHARS 104 set -- /d?* 105 IFS= # BUG_IFSGLOBC compat: eliminate glob chars from IFS before popping 106 pop -o noglob IFS 107 for v do 108 if str eq $v /dev; then 109 mustNotHave BUG_IFSGLOBP 110 return 111 fi 112 done 113 mustHave BUG_IFSGLOBP 114ENDT 115 116TEST title="'<>' redirection defaults to stdin" 117 (umask 077; putln ok >$tempdir/io-test8) 118 read v </dev/null <>$tempdir/io-test8 119 case $v in 120 ( ok ) mustNotHave BUG_REDIRIO ;; 121 ( '' ) mustHave BUG_REDIRIO ;; 122 ( * ) return 1 ;; 123 esac 124ENDT 125 126TEST title='redirs and assignments can be alternated' 127 # use 'eval' to delay parse error on zsh 5.0.x 128 (umask 077; eval 'v=1 >$tempdir/iotest9 v=2 2>&2 v=3 3>/dev/null v=4 putln ok' 2>/dev/null) 129 if ne $? 0; then 130 mustHave BUG_REDIRPOS 131 return 132 fi 133 read v <$tempdir/iotest9 134 str eq $v ok || mustHave BUG_REDIRPOS 135ENDT 136 137TEST title='comsubs work with stdout closed outside' 138 { 139 v=$(putln foo 5>/dev/null; command -v break; putln bar) 140 } >&- 141 case $v in 142 ( 'break' ) 143 # test that the documented BUG_CSUBSTDO workaround works 144 { 145 v=$(: 1>&1; putln foo 5>/dev/null; command -v break; putln bar) 146 } >&- 147 case $v in 148 ( foo${CCn}break${CCn}bar ) 149 mustHave BUG_CSUBSTDO ;; 150 ( * ) return 1 ;; 151 esac ;; 152 ( "foo${CCn}break${CCn}bar" ) 153 mustNotHave BUG_CSUBSTDO ;; 154 ( * ) return 1 ;; 155 esac 156ENDT 157 158TEST title='comsubs correctly strip final linefeeds' 159 v=${CCn}one$CCn$CCn$CCn` 160 false && putln nothing 161 `$CCn$CCn${CCn}two$CCn$CCn$CCn$( 162 true && putln something 163 )${CCn}three$CCn$CCn$CCn$CCn$CCn$( 164 : 165 )$CCn$CCn$CCn 166 case $v in 167 ( ${CCn}one$CCn$CCn$CCn$CCn$CCn${CCn}two$CCn$CCn${CCn}something${CCn}three$CCn$CCn$CCn$CCn$CCn$CCn$CCn$CCn ) 168 mustNotHave BUG_CSUBRMLF ;; 169 ( ${CCn}one$CCn$CCn${CCn}two$CCn$CCn${CCn}something${CCn}three$CCn$CCn$CCn ) 170 mustHave BUG_CSUBRMLF ;; 171 ( * ) return 1 ;; 172 esac 173ENDT 174 175TEST title='comsubs strip final linefeeds (here-doc)' 176 v=$( 177 thisshellhas BUG_HDOCMASK && umask 077 178 cat <<-end_of_heredoc 179 180 one 181 182 183 $(false && putln nothing) 184 185 186 two 187 188 189 $(true && putln something) 190 three 191 192 193 194 195 ` : ` 196 197 x 198 end_of_heredoc 199 ) 200 case $v in 201 ( ${CCn}one$CCn$CCn$CCn$CCn$CCn${CCn}two$CCn$CCn${CCn}something${CCn}three$CCn$CCn$CCn$CCn$CCn$CCn${CCn}x ) 202 mustNotHave BUG_CSUBRMLF ;; 203 ( ${CCn}one$CCn$CCn${CCn}two$CCn$CCn${CCn}something${CCn}three$CCn${CCn}x ) 204 mustHave BUG_CSUBRMLF ;; 205 ( * ) return 1 ;; 206 esac 207ENDT 208 209TEST title="put/putln check I/O with SIGPIPE ignored" 210 v=$( 211 set +x 212 { 213 ( 214 command trap "" PIPE 215 v=0 216 while let "(v += 1) < 250"; do 217 putln pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp \ 218 pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp \ 219 pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp \ 220 pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp || exit 221 done 2>/dev/null 222 putln BUG >&2 223 ) | true # pipe into cmd that does not read input; I/O error should occur when pipe buffer is full 224 } 2>&1 225 ) 226 case $v in 227 ( BUG ) mustHave BUG_PUTIOERR ;; 228 ( '' ) mustNotHave BUG_PUTIOERR ;; 229 ( * ) shellquote -f failmsg=$v; return 1 ;; 230 esac 231ENDT 232