Re: [PATCH] t4014: shell portability fix
- From
Jeff King <peff@peff.net>
- Date
- Jun 1, 2016, 07:04 UTC
- Message-ID
- <20160601070425.GA13648@sigill.intra.peff.net>
- In-Reply-To
- <xmqqshwx1k0p.fsf@gitster.mtv.corp.google.com>
On Tue, May 31, 2016 at 11:49:10PM -0700, Junio C Hamano wrote:
Show 17 quoted lines
> Jeff King <peff@peff.net> writes:
>
> > OK, last email tonight, I promise.
> >
> > Here's the subshell version. I'm a little embarrassed not to have
> > thought of it sooner (though the other one was a fun exercise).
> >
> > test_env () {
> > (
> > while test $# -gt 0
> > do
> > case "$1" in
> > *=*)
> > eval "${1%%=*}=\${1#*=}"
>
> Is this an elaborate way to say 'eval "$1"', or is there anything
> more subtle going on?Notice that the value half isn't expanded until we get inside the eval. So:
$ good() { eval "${1%%=*}=\${1#*=}"; }
$ bad() { eval "$1"; }
$ good foo="funny variable"; echo $foo
funny variable
$ bad foo="funny variable"
bash: variable: command not foundShow 5 quoted lines
> > *) > > "$@" > > exit > > ... or 'exec "$@"'
You can't exec a function, AFAIK (and that was the point of this exercise).
> > )
> > }
>
> You can dedent the whole thing and remove the outermost {} pair.True. I didn't know about that until recently. Is it portable everywhere?
Here's the patch I wrote up earlier (but was too timid to send out after my barrage of emails :) ).
It doesn't have the dedent, but I don't mind if you want to tweak it.
-- >8 -- Subject: test-lib: add in-shell "env" replacement
The one-shot environment variable syntax:
FOO=BAR some-program
is unportable when some-program is actually a shell function, like test_must_fail (on some shells FOO remains set after the function returns, and on others it does not).
We sometimes get around this by using env, like:
test_must_fail env FOO=BAR some-program
But that only works because test_must_fail's arguments are themselves a command which can be run. You can't run:
env FOO=BAR test_must_fail some-program
because env does not know about our shell functions. So there is no equivalent for test_commit, for example, and one must resort to:
(
FOO=BAR
export FOO
test_commit
)which is a bit verbose. Let's add a version of "env" that works _inside_ the shell, by creating a subshell, exporting variables from its argument list, and running the command.
Its use is demonstrated on a currently-unportable case in t4014.
Signed-off-by: Jeff King <peff@peff.net> --- t/t4014-format-patch.sh | 2 +- t/test-lib-functions.sh | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-)
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh index 8049cad..805dc90 100755 --- a/t/t4014-format-patch.sh +++ b/t/t4014-format-patch.sh @@ -1072,7 +1072,7 @@ test_expect_success '--from omits redundant in-body header' ' ' test_expect_success 'in-body headers trigger content encoding' ' - GIT_AUTHOR_NAME="éxötìc" test_commit exotic && + test_env GIT_AUTHOR_NAME="éxötìc" test_commit exotic && test_when_finished "git reset --hard HEAD^" && git format-patch -1 --stdout --from >patch && cat >expect <<-\EOF && diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh index 3978fc0..48884d5 100644 --- a/t/test-lib-functions.sh +++ b/t/test-lib-functions.sh @@ -939,3 +939,25 @@ mingw_read_file_strip_cr_ () { eval "$1=\$$1\$line" done } + +# Like "env FOO=BAR some-program", but run inside a subshell, which means +# it also works for shell functions (though those functions cannot impact +# the environment outside of the test_env invocation). +test_env () { + ( + while test $# -gt 0 + do + case "$1" in + *=*) + eval "${1%%=*}=\${1#*=}" + eval "export ${1%%=*}" + shift + ;; + *) + "$@" + exit + ;; + esac + done + ) +}
-- 2.9.0.rc0.174.g479a78d