On Tue, May 31, 2016 at 10:31:59PM -0400, Jeff King wrote:

> On Tue, May 31, 2016 at 08:09:43PM -0400, Eric Sunshine wrote:
> 
> > I was under the impression that the project was moving toward 'env' to
> > deal[1] with this sort of issue.
> > 
> > [1]: 512477b (tests: use "env" to run commands with temporary env-var
> > settings, 2014-03-18)
> 
> We can use it with test_must_fail, because it takes a command name:
> 
>   test_must_fail env FOO=BAR whatever-you-would-have-run
> 
> But I don't think it works in the general case, as test_commit does not
> run its arguments. So you'd want something like:
> 
>   env FOO=BAR test_commit exotic
> 
> but of course that doesn't work because "env" can't find the shell
> function. I think with bash you could do:
> 
>   export test_commit
>   env FOO=BAR bash -c test_commit exotic
> 
> but we can't rely on that.

I wondered if we could implement our own "env" in the shell, but it's a
little non-trivial, because:

  - our basic tool for setting variable-named variables is "eval", which
    means we need an extra layer of quoting

  - we have to restore the variables after. That means telling the
    difference between unset and empty (possible with "-" versus ":-", I
    think), but also the difference between unexported and exported
    (maybe possible by parsing "export -p", but I'd be shocked if that
    doesn't run into portability problems).

Here's what I came up with, which does seem to work. It's pretty gnarly,
though.

-- >8 --
# possible without a sub-program?
# portability issues with no-newline and sed?
shellquote () {
        printf "'"
        printf '%s' "$1" | sed "s/'/'\\\\''/g"
        printf "'"
}

# is there a simpler way, even when the contents are "unset"?
is_unset () {
        eval "test -z \"\${$1}\" && test unset = \"\${$1-unset}\""
}

# probably not portable; also, possible without sub-program?
is_exported () {
        export -p | grep "^declare -x $1="
}

# just a syntactic convenience
add_to () {
        eval "$1=\"\$$1
                \$2\""
}

fake_env () {
        fake_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
                        fake_env_var_=${1%%=*}
                        eval "fake_env_orig_=\$$fake_env_var_"
                        fake_env_val_=${1#*=}
                        shift

                        # unset value and clear export flag...
                        add_to fake_env_restore_ "unset $fake_env_var_"
                        # ...and then restore what was there, if anything
                        if ! is_unset "$fake_env_var_"
                        then
                                add_to fake_env_restore_ \
                                        "$fake_env_var_=$(shellquote 
"$fake_env_orig_")"
                                if is_exported "$fake_env_var_"
                                then
                                        add_to fake_env_restore_ \
                                                "export $fake_env_var_"
                                fi
                        fi

                        eval "$fake_env_var_=$(shellquote "$fake_env_val_")"
                        eval "export $fake_env_var_"
                        ;;
                *)
                        "$@"
                        fake_env_ret_=$?
                        eval "$fake_env_restore_"
                        return $fake_env_ret_
                        ;;
                esac
        done
}

# simple exercise
show() {
        echo >&2 "$1: myvar=$myvar"
}

myvar="horrible \"\\' mess"
show before
fake_env myvar="temporary $myvar" show during
show after

-Peff
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to