'Bash chaining commands with vs without semi-colon

Observed an interesting bash behavior that I don't understand.

Basically, when chaining commands in bash, whether there's a semi-colon between the two commands seem to make a difference in bash behavior. In particular the two examples below, one can read the env variable in Python, the other cannot

yuli@yuli-desktop:~$ A=1; python3
Python 3.8.10 (default, Nov 26 2021, 20:14:08)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> os.environ['A']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.8/os.py", line 675, in __getitem__
    raise KeyError(key) from None
KeyError: 'A'
>>> exit()

In a new bash process:

yuli@yuli-desktop:~$  A=1 python3
Python 3.8.10 (default, Nov 26 2021, 20:14:08)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>>
>>>
>>> os.environ['A']
'1'


Solution 1:[1]

You're not chaining two commands in the second example.

When you run a command in bash, you can set per-command environment variables by prefixing the command line with variable assignments, as in:

A=1 B=2 COLOR=red python3

In this case, those variables (A, B, COLOR) will be available in the environment of the command:

>>> os.getenv('COLOR')
'red'

In the first example, the semicolon splits the command line into two independent commands. Since the python3 command has no variable assignments associated with it, you're not setting any environment variables. Instead, you've set a shell-local variable A that would be visible in the shell but not in any child processes.

You can see documentation about this in the "SHELL GRAMMAR" section of the bash man page.


In comments, ErikMD mentions the export shell built-in, which is used to set persistent environment variables:

$ export COLOR=red

Or:

$ COLOR=red
$ export COLOR

An environment variable set this way will persist until explicitly unset and will be available in all subsequent commands. Contrast that with a variable set as part of a command invocation (as discussed earlier in this answer), which is only available for the duration of that command.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1