30

I can run ENV_VAR=value command to run command with a specific value for ENV_VAR. What is the equivalent to unset ENV_VAR for command?

2
  • 7
    Aside: command is an unfortunate choice of placeholders, since there actually is a built-in command by that name. mycommand or somecommand or such might be a better habit to get in. Aug 28, 2017 at 17:13
  • @CharlesDuffy, I usually use cmd for that. Aug 29, 2017 at 12:27

3 Answers 3

41

Other than with the -i option, which wipes the whole environment, POSIX env doesn't provide any way to unset a variable.

However, with a few env implementations (including GNU's, busybox' and FreeBSD's at least), you can do:

env -u ENV_VAR command

Which would work at removing every instance of an ENV_VAR variable from the environment (note though that it doesn't work for the environment variable with an empty name (env -u '' either gives an error or is ineffective depending on the implementation even though all accept env '=value', probably a limitation incurred by the unsetenv() C function which POSIX requires to return an error for the empty string, while there's no such limitation for putenv())).

Portably (in POSIX shells), you can do:

(unset -v ENV_VAR; exec command)

(note that with some shells, using exec can change which command is run: runs the one in the filesystem instead of a function or builtin for instance (and would bypass alias expansion obviously), like env above. You'd want to omit it in those cases).

But that won't work for environment variables that have a name that is not mappable to a shell variable (note that some shells like mksh would strip those variables from the environment on startup anyway), or variables that have been marked read-only.

-v is for the Bourne shell and bash whose unset without -v could unset a ENV_VAR function if there was no variable by that name. Most other shells wouldn't unset functions unless you pass the -f option. (unlikely to make a difference in practice).

(Also beware of the bug/misfeature of bash/mksh/yash whose unset, under some circumstance may not unset the variable, but reveal the variable in an outer scope)

If perl is available, you could do:

perl -e 'delete $ENV{shift@ARGV}; exec @ARGV or die$!' ENV_VAR command

Which will work even for the environment variable with an empty name.

Now all those won't work if you want to change an environment variable for a builtin or function and don't want them to run in a subshell, like in bash:

env -u LANG printf -v var %.3f 1.2 # would run /usr/bin/printf instead
(unset -v LANG; printf -v var %.3f 1.2) # changes to $var lost afterwards

(here unsetting LANG as a misguided approach at making sure . is used and understood as the decimal separator. It would be better to use LC_ALL=C printf... for that)

With some shells, you can instead create a local scope for the variable using a function:

without() {
  local "$1" # $1 variable declared as initially unset in bash¹
  shift
  "$@"
}
without LANG printf -v var %.3f 1.2

With zsh, you can also use an anonymous function:

(){local ENV_VAR; command}

That approach won't work in some shells (like the ones based on the Almquist shell), whose local don't declare the variable as initially unset (but inherit the value and attributes). In those, you can do local ENV_VAR; unset ENV_VAR, but don't do that in mksh or yash (typeset instead of local for that one) as that wouldn't work, the unset would only be cancelling the local.

¹ Also beware that in bash, the local ENV_VAR (even though unset) would retain the export attribute. So if command was a function that assigns a value to ENV_VAR, the variable would be available in the environment of commands called afterwards. unset ENV_VAR would clear that attribute. Or you could use local +x ENV_VAR which would also ensure a clean slate (unless that variable has been declared read-only, but then there's nothing you can do about it in bash).

2
  • 2
    It's a separate question, but what the heck is an environment variable with an empty name and how would you ever use it - even for some sort of exploit? Wouldn't you have to write a program that reads the environment and specifically looks for something like this?
    – Joe
    Sep 2, 2017 at 6:33
  • 1
    @Joe, try for instance env '=foo' python -c 'import os; print os.environ[""]'. I don't expect many people would want to set a variable like that. Sep 2, 2017 at 23:01
11

There’s no direct equivalent as far as I’m aware; you can use a subshell:

(unset ENV_VAR; exec command)
3
  • (unset ENV_VAR; exec somecommand) if you want to be equivalent to the original in efficiency (by consuming the subshell). As it is, this adds an extra fork. Aug 28, 2017 at 17:12
  • 1
    @Charles indeed, although some shells do that automatically since command is the last command in the subshell invocation. Aug 28, 2017 at 19:01
  • +1 because this way is simple to type for interactive use. I use subshells for one-off changes to the shell environment for a one-liner instead of remembering that it's env -u. Aug 29, 2017 at 0:37
1

You can use ENV_VAR= command

This doesnt actually unset the variable, but it may be useful in many cases (shell scripts usually just use test -n to check if a variable is set):

$ export ENV_VAR=foo
$ mycommand
ENV_VAR: foo
$ ENV_VAR=bar mycommand
ENV_VAR: bar
$ ENV_VAR= mycommand
ENV_VAR: 

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .