On Tue, May 31, 2016 at 11:44:13PM -0400, Jeff King wrote: > So this is gross, but I think it actually _is_ portable, with the > exception of the "is it exported" check. Hmm. So after thinking on this, I realized we don't have to do the clean-up ourselves at all, if we simply operate in a subshell. That means that any shell functions we call couldn't mutate the state, but that's probably an acceptable compromise, if the goal is to behave like env except for being able to call shell functions. Here is the "final" version of the more complicated scheme I came up with. That I think should be fairly portable, but the subshell thing is probably way less gross. -- >8 -- test_var_is_set () { eval "test -n \"\${$1+set}\"" } test_var_is_exported () { sh -c "test -n \"\${$1+set}\"" } # set var named by $1 to contents of $2 test_set_var () { eval "$1=\$2" } # set var named by $1 to contents of var named by $2 test_copy_var () { eval "test_set_var \$1 \"\$$2\"" } # just a syntactic convenience add_to () { eval "$1=\"\$$1 \$2\"" } test_env () { test_env_restore_= while test $# -gt 0 do case "$1" in *=*) # this whole thing is not safe when the var name has # spaces or other meta-characters, but since the names # all come from our test scripts, that should be OK test_env_var_=${1%%=*} test_env_orig_=test_env_orig_$test_env_var_ test_copy_var $test_env_orig_ $test_env_var_ test_env_val_=${1#*=} shift # unset value and clear export flag... add_to test_env_restore_ "unset $test_env_var_" # ...and then restore what was there, if anything if test_var_is_set "$test_env_var_" then add_to test_env_restore_ \ "test_copy_var $test_env_var_ $test_env_orig_" if test_var_is_exported "$test_env_var_" then add_to test_env_restore_ \ "export $test_env_var_" fi fi # and then clean up our temp variable add_to test_env_restore_ "unset $test_env_orig_" test_set_var $test_env_var_ "$test_env_val_" eval "export $test_env_var_" ;; *) "$@" test_env_ret_=$? eval "$test_env_restore_" return $test_env_ret_ ;; esac done } # simple exercise show () { echo >&2 "$1: here=$myvar ${myvar+(set)} sub=$(sh -c 'echo "$myvar ${myvar+(set)}"')" } doit () { echo "==> $1" show before test_env myvar="temporary $myvar" show during show after } unset myvar doit unset myvar="horrible \"\\' mess" doit with-value export myvar doit exported-with-value myvar= export myvar doit exported-but-blank -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html