git/list[1] front-page[2] threads[3] people[4] search[5] about
 

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 found
Show 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
Previous: Junio C HamanoNext: Junio C Hamano
Message 12 of 20 in “t4014: shell portability fix”
  1. t4014: shell portability fixJunio C Hamano, May 31, 2016
  2. Jeff KingMay 31, 2016
  3. Eric SunshineJun 1, 2016
  4. Jeff KingJun 1, 2016
  5. Jeff KingJun 1, 2016
  6. Jeff KingJun 1, 2016
  7. Jeff KingJun 1, 2016
  8. Jeff KingJun 1, 2016
  9. Jeff KingJun 1, 2016
  10. Junio C HamanoJun 1, 2016
  11. Junio C HamanoJun 1, 2016
  12. Jeff KingJun 1, 2016
  13. Junio C HamanoJun 1, 2016
  14. Jeff KingJun 1, 2016
  15. Junio C HamanoJun 1, 2016
  16. Jeff KingJun 1, 2016
  17. Johannes SixtJun 1, 2016
  18. Jeff KingJun 1, 2016
  19. Eric SunshineJun 1, 2016
  20. Ramsay JonesJun 1, 2016

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.