| 1 | ## compare_shells: bash dash mksh zsh ash
 | 
| 2 | ## oils_failures_allowed: 0
 | 
| 3 | 
 | 
| 4 | #### echo keyword
 | 
| 5 | echo done
 | 
| 6 | ## stdout: done
 | 
| 7 | 
 | 
| 8 | #### if/else
 | 
| 9 | if false; then
 | 
| 10 |   echo THEN
 | 
| 11 | else
 | 
| 12 |   echo ELSE
 | 
| 13 | fi
 | 
| 14 | ## stdout: ELSE
 | 
| 15 | 
 | 
| 16 | #### Turn an array into an integer.
 | 
| 17 | a=(1 2 3)
 | 
| 18 | (( a = 42 )) 
 | 
| 19 | echo $a
 | 
| 20 | ## stdout: 42
 | 
| 21 | ## N-I dash/ash stdout-json: ""
 | 
| 22 | ## N-I dash/ash status: 2
 | 
| 23 | 
 | 
| 24 | 
 | 
| 25 | #### assign readonly -- one line
 | 
| 26 | readonly x=1; x=2; echo hi
 | 
| 27 | ## status: 1
 | 
| 28 | ## OK dash/mksh/ash status: 2
 | 
| 29 | ## STDOUT:
 | 
| 30 | ## END
 | 
| 31 | 
 | 
| 32 | #### assign readonly -- multiple lines
 | 
| 33 | readonly x=1
 | 
| 34 | x=2
 | 
| 35 | echo hi
 | 
| 36 | ## status: 1
 | 
| 37 | ## OK dash/mksh/ash status: 2
 | 
| 38 | ## STDOUT:
 | 
| 39 | ## END
 | 
| 40 | ## BUG bash status: 0
 | 
| 41 | ## BUG bash STDOUT:
 | 
| 42 | hi
 | 
| 43 | ## END
 | 
| 44 | 
 | 
| 45 | #### assign readonly -- multiple lines -- set -o posix
 | 
| 46 | set -o posix
 | 
| 47 | readonly x=1
 | 
| 48 | x=2
 | 
| 49 | echo hi
 | 
| 50 | ## status: 1
 | 
| 51 | ## OK dash/mksh/ash status: 2
 | 
| 52 | ## STDOUT:
 | 
| 53 | ## END
 | 
| 54 | 
 | 
| 55 | #### unset readonly -- one line
 | 
| 56 | readonly x=1; unset x; echo hi
 | 
| 57 | ## STDOUT:
 | 
| 58 | hi
 | 
| 59 | ## END
 | 
| 60 | ## OK dash/ash status: 2
 | 
| 61 | ## OK zsh status: 1
 | 
| 62 | ## OK dash/ash stdout-json: ""
 | 
| 63 | ## OK zsh stdout-json: ""
 | 
| 64 | 
 | 
| 65 | #### unset readonly -- multiple lines
 | 
| 66 | readonly x=1
 | 
| 67 | unset x
 | 
| 68 | echo hi
 | 
| 69 | ## OK dash/ash status: 2
 | 
| 70 | ## OK zsh status: 1
 | 
| 71 | ## OK dash/ash stdout-json: ""
 | 
| 72 | ## OK zsh stdout-json: ""
 | 
| 73 | 
 | 
| 74 | #### First word like foo$x() and foo$[1+2] (regression)
 | 
| 75 | 
 | 
| 76 | # Problem: $x() func call broke this error message
 | 
| 77 | foo$identity('z')
 | 
| 78 | 
 | 
| 79 | foo$[1+2]
 | 
| 80 | 
 | 
| 81 | echo DONE
 | 
| 82 | 
 | 
| 83 | ## status: 2
 | 
| 84 | ## OK mksh/zsh status: 1
 | 
| 85 | ## STDOUT:
 | 
| 86 | ## END
 | 
| 87 | 
 | 
| 88 | #### Function names
 | 
| 89 | foo$x() {
 | 
| 90 |   echo hi
 | 
| 91 | }
 | 
| 92 | 
 | 
| 93 | foo $x() {
 | 
| 94 |   echo hi
 | 
| 95 | }
 | 
| 96 | 
 | 
| 97 | ## status: 2
 | 
| 98 | ## OK mksh/zsh status: 1
 | 
| 99 | ## BUG zsh status: 0
 | 
| 100 | ## STDOUT:
 | 
| 101 | ## END
 | 
| 102 | 
 | 
| 103 | 
 | 
| 104 | #### file with NUL byte
 | 
| 105 | echo -e 'echo one \0 echo two' > tmp.sh
 | 
| 106 | $SH tmp.sh
 | 
| 107 | ## STDOUT:
 | 
| 108 | one echo two
 | 
| 109 | ## END
 | 
| 110 | ## OK osh STDOUT:
 | 
| 111 | one
 | 
| 112 | ## END
 | 
| 113 | ## N-I dash stdout-json: ""
 | 
| 114 | ## N-I dash status: 127
 | 
| 115 | ## OK bash stdout-json: ""
 | 
| 116 | ## OK bash status: 126
 | 
| 117 | ## OK zsh stdout-json: "one \u0000echo two\n"
 | 
| 118 | 
 | 
| 119 | #### fastlex: PS1 format string that's incomplete / with NUL byte
 | 
| 120 | case $SH in bash) exit ;; esac
 | 
| 121 | 
 | 
| 122 | x=$'\\D{%H:%M'  # leave off trailing }
 | 
| 123 | echo x=${x@P}
 | 
| 124 | 
 | 
| 125 | ## STDOUT:
 | 
| 126 | x=\D{%H:%M
 | 
| 127 | ## END
 | 
| 128 | 
 | 
| 129 | # bash just ignores the missing }
 | 
| 130 | ## BUG bash stdout-json: ""
 | 
| 131 | 
 | 
| 132 | # These shells don't understand @P
 | 
| 133 | 
 | 
| 134 | ## N-I dash/ash stdout-json: ""
 | 
| 135 | ## N-I dash/ash status: 2
 | 
| 136 | 
 | 
| 137 | ## N-I zsh stdout-json: ""
 | 
| 138 | ## N-I zsh status: 1
 | 
| 139 | 
 | 
| 140 | 
 | 
| 141 | #### 'echo' and printf fail on writing to full disk
 | 
| 142 | 
 | 
| 143 | # Inspired by https://blog.sunfishcode.online/bugs-in-hello-world/
 | 
| 144 | 
 | 
| 145 | echo hi > /dev/full
 | 
| 146 | echo status=$?
 | 
| 147 | 
 | 
| 148 | printf '%s\n' hi > /dev/full
 | 
| 149 | echo status=$?
 | 
| 150 | 
 | 
| 151 | ## STDOUT:
 | 
| 152 | status=1
 | 
| 153 | status=1
 | 
| 154 | ## END
 | 
| 155 | 
 | 
| 156 | #### other builtins fail on writing to full disk
 | 
| 157 | 
 | 
| 158 | type echo > /dev/full
 | 
| 159 | echo status=$?
 | 
| 160 | 
 | 
| 161 | # other random builtin
 | 
| 162 | ulimit -a > /dev/full
 | 
| 163 | echo status=$?
 | 
| 164 | 
 | 
| 165 | ## STDOUT:
 | 
| 166 | status=1
 | 
| 167 | status=1
 | 
| 168 | ## END
 | 
| 169 | 
 | 
| 170 | ## BUG mksh/zsh STDOUT:
 | 
| 171 | status=0
 | 
| 172 | status=0
 | 
| 173 | ## END
 | 
| 174 | 
 | 
| 175 | #### subshell while running a script (regression)
 | 
| 176 | # Ensures that spawning a subshell doesn't cause a seek on the file input stream
 | 
| 177 | # representing the current script (issue #1233).
 | 
| 178 | cat >tmp.sh <<'EOF'
 | 
| 179 | echo start
 | 
| 180 | (:)
 | 
| 181 | echo end
 | 
| 182 | EOF
 | 
| 183 | $SH tmp.sh
 | 
| 184 | ## STDOUT:
 | 
| 185 | start
 | 
| 186 | end
 | 
| 187 | ## END
 | 
| 188 | 
 | 
| 189 | #### for loop (issue #1446)
 | 
| 190 | case $SH in (dash|mksh|ash) exit ;; esac
 | 
| 191 | 
 | 
| 192 | for (( n=0; n<(3-(1)); n++ )) ; do echo $n; done
 | 
| 193 | 
 | 
| 194 | ## STDOUT:
 | 
| 195 | 0
 | 
| 196 | 1
 | 
| 197 | ## END
 | 
| 198 | ## N-I dash/mksh/ash STDOUT:
 | 
| 199 | ## END
 | 
| 200 | 
 | 
| 201 | 
 | 
| 202 | 
 | 
| 203 | #### for loop 2 (issue #1446)
 | 
| 204 | case $SH in (dash|mksh|ash) exit ;; esac
 | 
| 205 | 
 | 
| 206 | 
 | 
| 207 | for (( n=0; n<(3- (1)); n++ )) ; do echo $n; done
 | 
| 208 | 
 | 
| 209 | ## STDOUT:
 | 
| 210 | 0
 | 
| 211 | 1
 | 
| 212 | ## END
 | 
| 213 | ## N-I dash/mksh/ash STDOUT:
 | 
| 214 | ## END
 | 
| 215 | 
 | 
| 216 | #### autoconf word split (#1449)
 | 
| 217 | 
 | 
| 218 | mysed() {
 | 
| 219 |   for line in "$@"; do
 | 
| 220 |     echo "[$line]"
 | 
| 221 |   done
 | 
| 222 | }
 | 
| 223 | 
 | 
| 224 | sedinputs="f1 f2"
 | 
| 225 | sedscript='my sed command'
 | 
| 226 | 
 | 
| 227 | # Parsed and evaluated correctly: with word_part.EscapedLiteral \"
 | 
| 228 | 
 | 
| 229 | x=$(eval "mysed -n \"\$sedscript\" $sedinputs")
 | 
| 230 | echo '--- $()'
 | 
| 231 | echo "$x"
 | 
| 232 | 
 | 
| 233 | # With backticks, the \" gets lost somehow
 | 
| 234 | 
 | 
| 235 | x=`eval "mysed -n \"\$sedscript\" $sedinputs"`
 | 
| 236 | echo '--- backticks'
 | 
| 237 | echo "$x"
 | 
| 238 | 
 | 
| 239 | 
 | 
| 240 | # Test it in a case statement
 | 
| 241 | 
 | 
| 242 | case `eval "mysed -n \"\$sedscript\" $sedinputs"` in 
 | 
| 243 |   (*'[my sed command]'*)
 | 
| 244 |     echo 'NOT SPLIT'
 | 
| 245 |     ;;
 | 
| 246 | esac
 | 
| 247 | 
 | 
| 248 | ## STDOUT:
 | 
| 249 | --- $()
 | 
| 250 | [-n]
 | 
| 251 | [my sed command]
 | 
| 252 | [f1]
 | 
| 253 | [f2]
 | 
| 254 | --- backticks
 | 
| 255 | [-n]
 | 
| 256 | [my sed command]
 | 
| 257 | [f1]
 | 
| 258 | [f2]
 | 
| 259 | NOT SPLIT
 | 
| 260 | ## END
 | 
| 261 | 
 | 
| 262 | #### autoconf arithmetic - relaxed eval_unsafe_arith (#1450)
 | 
| 263 | 
 | 
| 264 | as_fn_arith ()
 | 
| 265 | {
 | 
| 266 |     as_val=$(( $* ))
 | 
| 267 | }
 | 
| 268 | as_fn_arith 1 + 1
 | 
| 269 | echo $as_val
 | 
| 270 | 
 | 
| 271 | ## STDOUT:
 | 
| 272 | 2
 | 
| 273 | ## END
 | 
| 274 | 
 | 
| 275 | #### command execution $(echo 42 | tee PWNED) not allowed
 | 
| 276 | 
 | 
| 277 | rm -f PWNED
 | 
| 278 | 
 | 
| 279 | x='a[$(echo 42 | tee PWNED)]=1'
 | 
| 280 | echo $(( x ))
 | 
| 281 | 
 | 
| 282 | if test -f PWNED; then
 | 
| 283 |   cat PWNED
 | 
| 284 | else
 | 
| 285 |   echo NOPE
 | 
| 286 | fi
 | 
| 287 | 
 | 
| 288 | ## status: 1
 | 
| 289 | ## OK dash/ash status: 2
 | 
| 290 | ## stdout-json: ""
 | 
| 291 | ## BUG bash/mksh/zsh status: 0
 | 
| 292 | ## BUG bash/mksh/zsh STDOUT:
 | 
| 293 | 1
 | 
| 294 | 42
 | 
| 295 | ## END
 | 
| 296 | 
 | 
| 297 | #### process sub <(echo 42 | tee PWNED) not allowed
 | 
| 298 | 
 | 
| 299 | rm -f PWNED
 | 
| 300 | 
 | 
| 301 | x='a[<(echo 42 | tee PWNED)]=1'
 | 
| 302 | echo $(( x ))
 | 
| 303 | 
 | 
| 304 | if test -f PWNED; then
 | 
| 305 |   cat PWNED
 | 
| 306 | else
 | 
| 307 |   echo NOPE
 | 
| 308 | fi
 | 
| 309 | 
 | 
| 310 | ## status: 1
 | 
| 311 | ## stdout-json: ""
 | 
| 312 | 
 | 
| 313 | ## OK dash/ash status: 2
 | 
| 314 | 
 | 
| 315 | # bash keeps going
 | 
| 316 | ## BUG bash status: 0
 | 
| 317 | ## BUG bash STDOUT:
 | 
| 318 | NOPE
 | 
| 319 | ## END
 | 
| 320 | 
 | 
| 321 | 
 | 
| 322 | #### unset doesn't allow command execution
 | 
| 323 | 
 | 
| 324 | typeset -a a  # for mksh
 | 
| 325 | a=(42)
 | 
| 326 | echo len=${#a[@]}
 | 
| 327 | 
 | 
| 328 | unset -v 'a[$(echo 0 | tee PWNED)]'
 | 
| 329 | echo len=${#a[@]}
 | 
| 330 | 
 | 
| 331 | if test -f PWNED; then
 | 
| 332 |   echo PWNED
 | 
| 333 |   cat PWNED
 | 
| 334 | else
 | 
| 335 |   echo NOPE
 | 
| 336 | fi
 | 
| 337 | 
 | 
| 338 | ## status: 1
 | 
| 339 | ## STDOUT:
 | 
| 340 | len=1
 | 
| 341 | ## END
 | 
| 342 | 
 | 
| 343 | ## N-I dash/ash status: 2
 | 
| 344 | ## N-I dash/ash stdout-json: ""
 | 
| 345 | 
 | 
| 346 | ## BUG bash/mksh status: 0
 | 
| 347 | ## BUG bash/mksh STDOUT:
 | 
| 348 | len=1
 | 
| 349 | len=0
 | 
| 350 | PWNED
 | 
| 351 | 0
 | 
| 352 | ## END
 | 
| 353 | 
 | 
| 354 | ## BUG zsh status: 0
 | 
| 355 | ## BUG zsh STDOUT:
 | 
| 356 | len=1
 | 
| 357 | len=1
 | 
| 358 | PWNED
 | 
| 359 | 0
 | 
| 360 | ## END
 | 
| 361 | 
 | 
| 362 | #### printf integer size bug
 | 
| 363 | 
 | 
| 364 | # from Koiche on Zulip
 | 
| 365 | 
 | 
| 366 | printf '%x\n' 2147483648
 | 
| 367 | printf '%u\n' 2147483648
 | 
| 368 | ## STDOUT:
 | 
| 369 | 80000000
 | 
| 370 | 2147483648
 | 
| 371 | ## END
 | 
| 372 | 
 | 
| 373 | #### (( status bug
 | 
| 374 | 
 | 
| 375 | # from Koiche on Zulip
 | 
| 376 | 
 | 
| 377 | case $SH in dash|ash) exit ;; esac
 | 
| 378 | 
 | 
| 379 | (( 1 << 32 ))
 | 
| 380 | echo status=$?
 | 
| 381 | 
 | 
| 382 | (( 1 << 32 )) && echo yes
 | 
| 383 | 
 | 
| 384 | ## STDOUT:
 | 
| 385 | status=0
 | 
| 386 | yes
 | 
| 387 | ## END
 | 
| 388 | 
 | 
| 389 | ## N-I dash/ash STDOUT:
 | 
| 390 | ## END
 |