48

What is the difference between the command

$ env FOO=bar baz

and

$ FOO=bar baz

What effect does env have?

5
  • 4
    Sort of a side question, but what is it the feature itself called when you set an environment variable for a single subcommand like that? I've always had a hard time finding info on this because I don't know what it's called. May 15, 2014 at 18:36
  • 1
    @JohnCromartie, you should ask that as a question.
    – cjm
    May 15, 2014 at 19:04
  • 1
    For bash, it's documented here: gnu.org/software/bash/manual/… May 15, 2014 at 19:24
  • 2
    @JohnCromartie It's an optional component of every shell command, so it is in the "Simple Commands" section of most shell manuals. For POSIX, that would be here. glenn linked the analogous section from the bash manual for you already.
    – jw013
    May 16, 2014 at 1:41
  • Setting a variable that does not exist via an assignment creates a shell variable. Setting it via ENV or exporting the variable pushes the variable into the shell's execution environment. Changing the value of an existing variable will update the execution environment value if one exists, otherwise change it in the shell internal variables.
    – Johan
    May 21, 2014 at 11:01

6 Answers 6

35

They are functionally equivalent.

The main difference is that env FOO=bar baz involves invoking an intermediary process between the shell and baz, where as with FOO=bar baz the shell directly invokes baz.
So in that regard, FOO=bar baz is preferred.

The only situations I find myself using env FOO=bar in is where I have to pass a command to another command.
As a specific example, lets say I have a wrapper script that performs some modifications of the environment, and then calls exec on the command that was passed to it, such as:

#!/bin/bash
FOO=bob
some stuff
exec "$@"

If you execute it as myscript FOO=bar baz, the exec will throw an error as exec FOO=bar baz is invalid.
Instead you call it as myscript env FOO=bar baz which gets executed as exec env FOO=bar baz, and is perfectly valid.

8
  • 2
    You can do FOO=bar exec baz though, so you don't need env in your last point. May 15, 2014 at 19:25
  • When you exec something, does it use your current environment? May 15, 2014 at 19:25
  • 2
    Ditto @StephaneChazelas, and you can also sudo FOO=bar baz to pass environment variables without the need for env. May 15, 2014 at 19:27
  • 3
    @StephaneChazelas that only works if I want to put FOO=bar in the script. If FOO isn't always bar, I don't want to hard code it, and instead pass it in.
    – phemmer
    May 15, 2014 at 19:27
  • @glennjackman yes it does, as long as the variables are exported or passed before the exec, such as FOO=bar exec baz.
    – phemmer
    May 15, 2014 at 19:28
17

In this particular example, there is no effective difference, assuming your shell is a POSIX-compatible shell, and assuming baz is an executable and not a shell builtin.

If your shell is not a POSIX-compatible shell, for example csh or tcsh, the syntax

FOO=bar baz

does not work, and there is no equivalent shell syntax. For those shells, the env command is the only way to override or inject environment variables for a single command.

If baz is a shell builtin, let's say fc for example, then env will not give the same results, because env is executing a new process instead of being run directly by the command shell. Moreover, there is no fc executable, it can only be run as a shell builtin because of the way it interacts with the shell environment, and so env will never work with a builtin like fc.

In addition, env offers the -i option, which allows you to start a command in an empty environment with only a specified set of environment variables. So env can be very useful for starting processes in sanitized environments, for example

env -i HOME=/tmp/homedir "PATH=`getconf PATH`" "TERM=$TERM" FOO=bar baz
1
  • When I used to use tcsh I would write (setenv FOO bar; baz) to get the equivalent function.
    – Barmar
    May 21, 2014 at 19:53
6

In addition to what has already been said

VAR=value cmd args > redirs

being a shell (Bourne/POSIX) feature, you're limited in the name of the environment variables you pass to cmd. They have to be valid shell variable names and must not be read-only or special variables to the shell.

For instance, you cannot do:

1=foo cmd

Or

+++=bar cmd

bash doesn't allow you to do:

SHELLOPTS=xtrace cmd

While you can do:

env 1=foo cmd
env +++=bar cmd
env '=baz' cmd

(not that you want or should want to do that). Or:

env SHELLOPTS=xtrace cmd

(I sometimes need to do that).

Note that with env you still cannot pass an environment variable string that doesn't contain a = (not that you would want to do that either).

3

One use of env is to allow $PATH searching of executables in shebang lines (because env considers the $PATH when searching for the executable). This is useful if the executable you want to invoke may be in different places on different machines. For example,

#!/usr/bin/env perl

in the first line of a script with executable bit set will execute this script with Perl no matter whether it is installed in /usr/bin/perl or in /usr/local/bin/perl or in a completely different place, as long as the directory is in the path.

Of course that path search comes with an added risk, but then, the risk is not larger than if you had explicitly written perl yourscript.pl, which also looks up perl in the search path.

2

Another time when env is really useful is if you want to control the environment completely. I run a server program (Informix, in case you can't guess) whose environment I want to control completely. I run it using env at the end of a script which sets a bunch of variables to the correct values:

env -i HOME="$IXD" \
       INFORMIXDIR="$IXD" \
       INFORMIXSERVER="$IXS" \
       ${IXC:+INFORMIXCONCSMCFG="$IXC"} \
       ${IXH:+INFORMIXSQLHOSTS="$IXH"} \
       IFX_LISTEN_TIMEOUT=3 \
       ONCONFIG="onconfig.$IXS" \
       PATH="/bin:/usr/bin:$IXD/bin" \
       SHELL=/bin/ksh \
       TZ=UTC0 \
    $ONINIT "$@"

The -i option zaps the existing environment. The subsequent VAR=value options set the environment variables that I want set; the name of the program is in $ONINIT, and any command line arguments are passed through verbatim with "$@".

The ${IXH:+INFORMIXSQLHOSTS="$IXH"} construct only passes INFORMIXSQLHOSTS="$IXH" to env if $IXH is set to a non-empty value.

0

env is an executable from GNU Coreutils, NOT a shell builtin

$ type env
env is hashed (/usr/bin/env)
$ which env
/usr/bin/env

$ env FOO=bar baz:
bash calls env, then env sets FOO to bar and calls baz

$ FOO=bar baz:
bash sets FOO to bar and calls baz directly

demo of this key difference:

$ FOO=bar /proc/self/exe --version
GNU bash, version 5.1.4(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$ env FOO=bar /proc/self/exe --version
env (GNU coreutils) 8.32
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Written by Richard Mlynarik, David MacKenzie, and Assaf Gordon.

You must log in to answer this question.

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