48

What is the difference between the command

$ env FOO=bar baz

and

$ FOO=bar baz

What effect does env have?

  • 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. – John Cromartie May 15 '14 at 18:36
  • 1
    @JohnCromartie, you should ask that as a question. – cjm May 15 '14 at 19:04
  • 1
    For bash, it's documented here: http://www.gnu.org/software/bash/manual/bashref.html#Simple-Command-Expansion – glenn jackman May 15 '14 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 '14 at 01: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 '14 at 11:01

6 Answers6

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.

phemmer
  • 71,831
  • 2
    You can do FOO=bar exec baz though, so you don't need env in your last point. – Stéphane Chazelas May 15 '14 at 19:25
  • When you exec something, does it use your current environment? – glenn jackman May 15 '14 at 19:25
  • 2
    Ditto @StephaneChazelas, and you can also sudo FOO=bar baz to pass environment variables without the need for env. – Michael Miller May 15 '14 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 '14 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 '14 at 19:28
  • @mtmiller you're right, bad example. There are other programs which you can't do that, but they're obscure. Thought sudo did and it would be a good example. My mistake :-( – phemmer May 15 '14 at 19:30
  • Sorry, I had missed your point. I thought you were saying that you can't pass env vars with exec. – Stéphane Chazelas May 15 '14 at 19:34
  • note that in an example like this, many shells will optimize the last command in a script into an exec automatically if it looks reasonable to do so. – Aaron Davies May 16 '14 at 17:22
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
  • When I used to use tcsh I would write (setenv FOO bar; baz) to get the equivalent function. – Barmar May 21 '14 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.

celtschk
  • 10,844
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.
Darren Ng
  • 131