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

0

3 Answers 3

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.

1
  • 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! Apr 18, 2012 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
5
  • I hadn't really thought about the difference between a shell vs environment variable. I'll have to go do some research. Thanks for the answer. Apr 26, 2012 at 17:02
  • why doesn't 'VAR1=123 echo $VAR1` work? Jan 17, 2021 at 6:08
  • @MuhammadUmer It works (there's no error or anything), it just doesn't do what you expect. It prints the value of VAR1 before the assignment, not the value after the assignment. See unix.stackexchange.com/questions/56444/… Jan 17, 2021 at 15:32
  • is there docs on VAR=value somecommand why shell var is getting treated like env var for somecommand? Jan 18, 2021 at 4:32
  • @MuhammadUmer Sure. For example the POSIX specification: “Variable assignments shall be performed as follows: (…)”. Or your shell's manual. Jan 18, 2021 at 12:51
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

You must log in to answer this question.

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