diff --git a/bash-env-json b/bash-env-json index 3ac7cc8..374bb32 100755 --- a/bash-env-json +++ b/bash-env-json @@ -129,9 +129,9 @@ function eval_or_source() { _path="$1" _error_file=$(mktemp -u) - # tell ShellCheck I really do want to expand this now and not at the point of exit - # shellcheck disable=SC2064 - trap "rm -f $_error_file" EXIT + touch "$_error_file" + exec 3<"$_error_file" 4>"$_error_file" + rm -f "$_error_file" if test -n "$_path"; then # source from file if specified @@ -142,19 +142,35 @@ function eval_or_source() { # ShellCheck can't cope with sourcing from an unknown path # shellcheck disable=SC1090 - if ! source "$_path" >/dev/null 2>"$_error_file"; then - emit_error_exit "$(head -1 "$_error_file")" + if ! source "$_path" >/dev/null 2>&4; then + exec 4>&- + emit_error_exit "$(head -1 <&3)" fi else # otherwise eval from stdin _source=$(/dev/null 2>"$_error_file"; then + if ! eval "$_source" >/dev/null 2>&4; then + exec 4>&- # discard error location, because it is this file not the one sourced - emit_error_exit "$(sed -e 's/^.*line\s*[0-9]*:\s*//' "$_error_file")" + emit_error_exit "$(sed -e 's/^.*line\s*[0-9]*:\s*//' <&3)" fi fi } +function invoke_safely() { + local _fn="$1" + + _error_file=$(mktemp -u) + touch "$_error_file" + exec 3<"$_error_file" 4>"$_error_file" + rm -f "$_error_file" + + "$_fn" >/dev/null 2>&4 || { + exec 4>&- + emit_error_exit "$(head -1 <&3)" + } +} + function get_args() { local -n _opt_path="$1" local -n _opt_shellfn_names="$2" @@ -203,39 +219,40 @@ function main() { eval_or_source "$_path" capture _env_current _shellvars_current - # validate all the functions we will invoke before committing to the happy path - for _fn in "${_shellfn_names[@]}"; do - test "$(type -t "$_fn")" == "function" || { - emit_error_exit "no such function: $_fn" - } - done + # accumulate result in a file until we know we are error-free + _result_file=$(mktemp -u) + touch "$_result_file" + exec 5<"$_result_file" 6>"$_result_file" + rm -f "$_result_file" - emit "{" env _env_previous _env_current - emit "," shellvars _shellvars_previous _shellvars_current + emit "{" env _env_previous _env_current >&6 + emit "," shellvars _shellvars_previous _shellvars_current >&6 test "${#_shellfn_names[@]}" -gt 0 && { - echo ",\"fn\":{" + echo ",\"fn\":{" >&6 } local _fn_comma="" for _fn in "${_shellfn_names[@]}"; do capture _env_previous _shellvars_previous - # execute the function - "$_fn" + invoke_safely "$_fn" capture _env_current _shellvars_current - echo "$_fn_comma\"$_fn\":" - emit "{" env _env_previous _env_current - emit "," shellvars _shellvars_previous _shellvars_current - echo "}" + echo "$_fn_comma\"$_fn\":" >&6 + emit "{" env _env_previous _env_current >&6 + emit "," shellvars _shellvars_previous _shellvars_current >&6 + echo "}" >&6 _fn_comma="," done test "${#_shellfn_names[@]}" -gt 0 && { - echo "}" + echo "}" >&6 } - echo "}" + echo "}" >&6 + exec 6>&- + + cat <&5 } function bad_usage() { diff --git a/tests/shell-function-error.env b/tests/shell-function-error.env new file mode 100644 index 0000000..60c9696 --- /dev/null +++ b/tests/shell-function-error.env @@ -0,0 +1,3 @@ +function f() { + whacky-doodle-doo +}