19

Can anyone explain why the semi-colon is necessary in order for the LANG to be seen as updated by bash?

Doesn't work:

> LANG=Ja_JP bash -c "echo $LANG"
en_US

Works:

> LANG=Ja_JP ; bash -c "echo $LANG"
Ja_JP

I'm working with both bash 4.1.10 on linux and the same version under cygwin

sr_
  • 15,384

3 Answers3

31

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.

jw013
  • 51,212
  • Great answer - thanks! Regarding the addition of UTF-8. I'm trying to test the locale handling of an application which needs to work on several platforms some of which are quite old. Between differences like this one (which thankfully you've explained) and differences on linux and cygwin I'm about to throw myself under a bus! – Richard Corden Apr 18 '12 at 14:36
7

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
0

This is the summary of what I gathered from research:

There are two types: env variables and shell variables.

An environment variable is available, in a program and its child programs/processes/subshells. A shell variable is only available in the current shell.

https://askubuntu.com/a/26322/326584

First how echo $VAR works

Whenever terminal/shell/bash sees $ symbol, it does something called "parameter expansion". Which means variables are replaced with values.

https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html

So if VAR had value 'hello' then echo $VAR becomes echo 'hello'.

which means this works...

TEST=123 
echo $TEST
// 123

but the following doesn't work because variable got replaced BEFORE the command was able to set the variable.

TEST2=999 echo $TEST2
// nothing...

but if you add the semicolon..

TEST2=999; echo $TEST2

it is same as..

TEST2=999
echo $TEST2

.. which works same as before.

And since shell variables are not passed to subprocesses/child, when you call a command it creates a new process, so...

TEST3=111 
node -e 'console.log(process.env.TEST3)' 

or

TEST3=111 
printenv TEST3

both print nothing. Shell variables are not inherited by chld processes. Use export to make shell variable, an enviornment variable..

export TEST3=111 
printenv TEST3
//111

There is one exception...

VAR=123 printenv VAR
VAR=123 VAR2=456 printenv VAR2 //even multiple vars

Basically if written it like this and right after call the command then it only temporarily sets environment var for that command. It doesn't even set shell var. Think of it as completely new syntax.

VAR=123 printenv VAR // 123
echo $VAR // nothing
VAR=123
echo $VAR // 123