What is the difference between an inline variable assignment and a regular one in Bash?

The format VAR=value command sets the variable VAR to have the value value in the environment of the command command. The spec section covering this is the Simple Commands. Specifically:

Otherwise, the variable assignments shall be exported for the execution environment of the command and shall not affect the current execution environment except as a side-effect of the expansions performed in step 4.

The format VAR=value; command sets the shell variable VAR in the current shell and then runs command as a child process. The child process doesn't know anything about the variables set in the shell process.

The mechanism by which a process exports (hint hint) a variable to be seen by child processes is by setting them in its environment before running the child process. The shell built-in which does this is export. This is why you often see export VAR=value and VAR=value; export VAR.

The syntax you are discussing is a short-form for something akin to:

VAR=value
export VAR
command
unset -v VAR

only without using the current process environment at all.


To complement Etan Reisner's helpful answer:

It's important to distinguish between shell variables and environment variables:

Note: The following applies to all POSIX-compatible shells; bash-specific extensions are marked as such.

A shell variable is a shell-specific construct that is limited to the shell that defines it (with the exception of subshells, which get their own copies of the current shell's variables),
whereas an environment variable is inherited by any child process created by the current process (shell), whether that child process is itself a shell or not.
Note that all-uppercase variable names should only be used for environment variables.

Either way, a child process only ever inherits copies of variables, whose modification (by the child) does not affect the parent.

  • All environment variables are also shell variables (the shell ensures that),
  • but the inverse is NOT true: shell variables are NOT environment variables, unless explicitly designated or inherited as such - this designation is called exporting.
    • note that the off-by-default -a shell option (set with set -a, or passed to the shell itself as a command-line option) can be used to auto-export all shell variables.

Thus,

  • any variables you create implicitly by assignment - e.g., TSAN_OPTIONS="suppressions=/somewhere/file" - are ONLY shell variables, but NOT ALSO environment variables,
  • EXCEPT - perhaps confusingly - when prepended directly to a command - e.g. TSAN_OPTIONS="suppressions=/somewhere/file" ./myprogram - in which case they are ONLY environment variables, only in effect for THAT COMMAND.
    • This is what Etan's answer describes.

Shell variables become environment variables as well under the following circumstances:

  • based on environment variables that the shell itself inherited, such as $HOME
  • shell variables created explicitly with export varName[=value] or, in bash, also with declare -x varName[=value]
    • by contrast, in bash, using declare without -x, or using local in a function, creates mere shell variables
  • shell variables created implicitly while the off-by-default -a shell option is in effect (with limited exceptions)

Once a shell variable is marked as exported - i.e., marked as an environment variable - any subsequent changes to the shell variable update the environment variable as well; e.g.:

export TSAN_OPTIONS  # creates shell variable *and* corresponding environment variable

# ...

TSAN_OPTIONS="suppressions=/somewhere/file" # updates *both* the shell and env. var.

  • export -p prints all environment variables
  • unset [-v] MYVAR undefines shell variable $MYVAR and also removes it as an environment variable, if applicable.
  • in bash:
    • You can "unexport" a given variable without also undefining it as a shell variable with export -n MYVAR - this removes MYVAR from the environment, but retains its current value as a shell variable.
    • declare -p MYVAR prints variable $MYVAR's current value along with its attributes; if the output starts with declare -x, $MYVAR is exported (is an environment variable)