#!/usr/bin/env bash # # Usage: # $SH ./runtime-errors.sh all # # Run with bash/dash/mksh/zsh. # # PARSE ERRORS # proc source_bad_syntax { cat >_tmp/bad-syntax.sh << """ if foo; echo ls; fi """ source _tmp/bad-syntax.sh } # NOTE: # - bash correctly reports line 25 (24 would be better) # - mksh: no line number # - zsh: line 2 of eval, which doesn't really help. # - dash: ditto, line 2 of eval proc eval_bad_syntax { var code = ''if foo; echo ls; fi'' eval "echo -- $code" } # # COMMAND ERRORS # proc no_such_command { set -o errexit ZZZZZ echo 'SHOULD NOT GET HERE' } proc no_such_command_commandsub { set -o errexit echo $[ZZZZZ] echo 'SHOULD NOT GET HERE' } proc no_such_command_heredoc { set -o errexit # Note: bash gives the line of the beginning of the here doc! Not the actual # line. # TODO: osh doesn't give any psition info. cat << """ one $[ZZZZZ] three """ echo 'SHOULD NOT GET HERE' } proc failed_command { set -o errexit false echo 'SHOULD NOT GET HERE' } proc pipefail { false | wc -l set -o errexit set -o pipefail false | wc -l echo 'SHOULD NOT GET HERE' } proc pipefail_func { set -o errexit -o pipefail proc f { cat # NOTE: If you call 'exit 42', there is no error message displayed! #exit 42 return 42 } echo hi | f | wc echo 'SHOULD NOT GET HERE' } # TODO: point to {. It's the same sas a subshell so you don't know exactly # which command failed. proc pipefail_group { set -o errexit -o pipefail echo hi | do { cat; sh -c 'exit 42'; } | wc echo 'SHOULD NOT GET HERE' } # TODO: point to ( proc pipefail_subshell { set -o errexit -o pipefail echo hi | shell {cat; sh -c 'exit 42'} | wc echo 'SHOULD NOT GET HERE' } # TODO: point to 'while' proc pipefail_while { set -o errexit -o pipefail seq 3 | while true { read line echo X $line X if test $line = 2 { sh -c 'exit 42' } } | wc echo 'SHOULD NOT GET HERE' } # Multiple errors from multiple processes proc pipefail_multiple { set -o errexit -o pipefail do { echo 'four'; sh -c 'exit 4'; } | do { echo 'five'; sh -c 'exit 5'; } | do { echo 'six'; sh -c 'exit 6'; } } # NOTE: This prints a WARNING in bash. Not fatal in any shell except zsh. proc control_flow { break continue echo 'SHOULD NOT GET HERE' } # # WORD ERRORS # proc nounset { set -o nounset echo $x echo 'SHOULD NOT GET HERE' } # # ARITHMETIC ERRORS # proc nounset_arith { set -o nounset echo $shExpr(' x ') echo 'SHOULD NOT GET HERE' } proc divzero { echo $shExpr(' 1 / 0 ') echo 'SHOULD NOT GET HERE' } proc divzero_var { var zero = '0' echo $shExpr(' 1 / zero ') echo 'SHOULD NOT GET HERE' } # Only dash flags this as an error. proc string_to_int_arith { var x = ''ZZZ'' echo $shExpr(' x + 5 ') set -o strict-arith echo $shExpr(' x + 5 ') echo 'SHOULD NOT GET HERE' } # Hm bash treats this as a fatal error proc string_to_hex { echo $shExpr(' 0xGG + 1 ') echo 'SHOULD NOT GET HERE' } # Hm bash treats this as a fatal error proc string_to_octal { echo $shExpr(' 018 + 1 ') echo 'SHOULD NOT GET HERE' } # Hm bash treats this as a fatal error proc string_to_intbase { echo $shExpr(' 16#GG ') echo 'SHOULD NOT GET HERE' } # # BOOLEAN ERRORS # # Only osh cares about this. proc string_to_int_bool { [[ a -eq 0 ]] set -o strict-arith [[ a -eq 0 ]] echo 'SHOULD NOT GET HERE' } # # TEST DRIVER # proc _run_test { var t = $1 echo "--------" echo " CASE: $t" # Run in subshell so the whole thing doesn't exit shell { $t } echo " STATUS: $Status" echo } proc all { # Can't be done inside a loop! _run_test control_flow for t in [\ no_such_command no_such_command_commandsub no_such_command_heredoc \ failed_command \ pipefail pipefail_group pipefail_subshell pipefail_func pipefail_while \ nonexistent nounset \ nounset_arith divzero divzero_var \ string_to_int_arith string_to_hex string_to_octal \ string_to_intbase string_to_int_bool] { _run_test $t } } @Argv