When to use a semi-colon between environment variables and a command

Parameter and other types of expansions are performed when the command is read, before it is executed.

The first version, LANG=Ja_JP bash -c "echo $LANG", is a single command. After it is parsed as such, $LANG is expanded to en_US before anything is executed. Once bash is finished processing the input, it forks a process, adds LANG=Ja_JP to the environment as expected, and then executes bash -c echo en_US.

You can prevent expansion with single quotes, i.e. LANG=Ja_JP bash -c 'echo $LANG' outputs Ja_JP.

Note that when you have a variable assignment as part of a command, the assignment only affects the environment of that command and not that of your shell.

The second version, LANG=Ja_JP; bash -c "echo $LANG" is actually two separate commands executed in sequence. The first is a simple variable assignment without a command, so it affects your current shell.

Thus, your two snippets are fundamentally different despite the superficial distinction of a single ;.

Completely off-topic, but might I recommend appending a .UTF-8 when setting LANG. There's no good reason nowadays not to be using Unicode in the 21st century.


VAR=value; somecommand is equivalent to

VAR=value
somecommand

These are unrelated commands executed one after the other. The first command assigns a value to the shell variable VAR. Unless VAR is already an environment variable, it is not exported to the environment, it remains internal to the shell. A statement export VAR would export VAR to the environment.

VAR=value somecommand is a different syntax. The assignment VAR=value is to the environment, but this assignment is only made in the execution environment of somecommand, not for the subsequent execution of the shell.

By way of example:

# Assume neither VAR1 nor VAR2 is in the environment
VAR1=value
echo $VAR1                        # displays "value"
env | grep '^VAR1='               # displays nothing
VAR2=value env | grep '^VAR2='    # displays "VAR2=value"
echo $VAR2                        # displays nothing