Unset an environment variable for a single command

In Bash, we can set an environment variable for a single command this way:

FOO=bar somecommand

What if we want to unset a variable for a single command?


Solution 1:

Technically, they're not environment variables until someone exports them. But you can at least set them to empty:

FOO= some command

If removing them from the environment is enough, you can use env:

env -u FOO somecommand

Solution 2:

env -u FOO somecommand

This will remove the environment variable FOO from the somecommand process' environment.

And to unset multiple variables:

env -u FOO -u FOO2 somecommand

Solution 3:

For anyone intending to run a command with none of their environment variables, you can do so by running:

env -i somecommand

Solution 4:

This is tricky when "somecommand" is a shell function.

One-shot environment variable assignments, such as 'FOO' in "FOO=bar cmd", exist only during the invocation of 'cmd'.
However, if 'cmd' happens to be a shell function, then 'FOO' is assigned in the executing shell itself, and that assignment remains until the process exits (unless explicitly unset).
Since this side-effect of "FOO=bar shell_func" is unlikely to be intentional, it should be avoided.

To further illustrates how the FOO= aCommand is dangerous, consider Git 2.26 (Q1 2020), which avoids "FOO= shell_function (unsetting FOO just for one command).

See commit d6509da, commit a7fbf12, commit c7973f2 (26 Dec 2019) by Jonathan Nieder (artagnon).
(Merged by Junio C Hamano -- gitster -- in commit c7372c9, 30 Jan 2020)

fetch test: avoid use of "VAR= cmd" with a shell function

Signed-off-by: Jonathan Nieder

Just like assigning a nonempty value, assigning an empty value to a shell variable when calling a function produces non-portable behavior: in some shells, the assignment lasts for the duration of the function invocation, and in others, it persists after the function returns.

Use an explicit subshell with the envvar exported to make the behavior consistent across shells and crystal clear.

All previous instances of this pattern used "VAR=value" (with nonempty value), which is already diagnosed automatically by "make test-lint" since a0a630192d (t/check-non-portable-shell: detect "FOO=bar shell_func", 2018-07-13).

For example, instead of:

GIT_TEST_PROTOCOL_VERSION= trace_fetch client origin to_fetch

Use a subshell:

(
    GIT_TEST_PROTOCOL_VERSION= &&
    export GIT_TEST_PROTOCOL_VERSION &&
    trace_fetch client origin to_fetch
) &&
...