When interacting with the server through a shell session, there are many pieces of information that the shell compiles to determine its behavior and access to resources. Some of these options are contained in the configuration options and others are determined by user input.
One way the shell keeps track of all these settings and details is through an area it maintains called environment. The environment is an area that the shell creates each time you log on that contains variables that define system properties.
In this guide, we will discuss how to interact with the environment and read or set environmental and shell variables interactively and through configuration files.
If you want to continue using your local system or a remote server, open a terminal and run the commands in this tutorial there.
How environmental and
environmental variables work
Each time a shell session is generated, a process is performed to collect and compile information that should be available to the shell process and its child processes. You get the data for these settings from a variety of different files and settings on the system.
The environment provides a means through which the shell process can obtain or set configurations and, in turn, pass them on to its child processes.
The environment is implemented as strings that represent key-value pairs. If multiple values are passed, they are usually separated by a colon (:) Characters. Each pair will usually look like this
: KEY = value 1: value 2: …
If the value contains significant white space, quotation marks are used:
KEY=”value with spaces”
The keys in these scenarios are variable. They can be of two types, environmental variables or shell variables.
Environment variables are variables defined for the current shell and inherited by any shell or child process. Environmental variables are used to pass information to processes that are generated from the shell.
Shell variables are
variables that are contained exclusively within the shell in which they were set or defined. They are often used to keep track of ephemeral data, such as the current working directory.
By convention, these types of variables are generally defined using all capital letters. This helps users distinguish environmental variables within other contexts.
shell and environment variables
Each shell session keeps track of its own shell and environment variables. We can access these in different ways.
We can see a list of all our environmental variables using the env or printenv commands. In their default state, they should work exactly the same
Your shell environment can have more or fewer variables set, with values other than the following
: OutputSHELL=/bin/bash TERM=xterm USER=demouser LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35: bd=40;33;01: cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca:… MAIL=/var/mail/demouser PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games PWD=/home/demouser LANG=en_US. UTF-8 SHLVL=1 HOME=/home/demouser LOGNAME=demouser LESSOPEN=| /usr/bin/lesspipe %s LESSCLOSE=/usr/bin/lesspipe %s %s _=/usr/bin/printenv
This is quite typical of printenv and env output. The difference between the two commands is only apparent in their more specific functionality. For example, with
printenv, you can request the values of individual variables:
- printenv PATH
On the other hand, env allows you to modify the environment in which programs run by passing a set of variable definitions to a command like this:
- env VAR1=”value” command_to_run command_options
Since, as we learned earlier, child processes typically inherit environmental variables from the parent process, this gives you the opportunity to override values or add additional variables for the child.
As you can see from the output of our printenv command, there are quite a few environmental variables set through our files and system processes without our input.
These show the environmental variables, but how do we see the shell variables?
The set command can be used for this. If we write set without any additional parameters, we will get a list of all shell variables, environment variables, local variables and shell functions
OutputBASH=/bin/bash BASHOPTS=checkwinsize:cmdhist:expand_aliases:extglob:extquote:force_fignore:histappend:interactive_comments:login_shell:progcomp:promptvars:sourcepath BASH_ALIASES=() BASH_ARGC=() BASH_ARGV=() BASH_CMDS=() . .
tag. This is usually a huge list. You probably want to funnel it into a pager program to more easily deal with the output amount:
- set | less
The amount of additional information we receive back is a bit overwhelming. We probably don’t need to know all the bash functions that are defined, for example.
We can clean up the output by specifying that the set should work in POSIX mode, which will not print shell functions. We can run this in a sub-shell so that it doesn’t change our current
- (set -o posix; set)
This will list
all the environment and shell variables that are defined.
We can try to compare this output with the output of the env or printenv commands to try to get a list of shell variables only, but this will be imperfect because of the different ways these commands generate information:
- comm -23 <(set -o posix;
set | sort) <(env | sort)
is likely to still include some environment variables, due to the fact that the set command generates quoted values, while the printenv and env commands do not cite string values
This should still give you a good idea of the environmental and shell variables that are set in your session.
These variables are used for all sorts of things. They provide an alternative way to set persistent values for the interprocess session, without writing changes to a file.
environmental and shell variables
Some environmental and shell variables are very useful and are mentioned quite often. Here are some common environmental variables you’ll encounter
- SHELL: This describes the shell that will interpret any command you type. In most cases, this will be bash by default, but other values can be set if you prefer other options.
- TERM: Specifies the type of terminal to emulate when running the shell. Different hardware terminals can be emulated for different operational requirements. However, usually, you won’t have to worry about this.
- USER: The user who is currently logged in.
- PWD: The current working directory.
- OLDPWD: The previous working directory. This is maintained by the shell in order to return to your previous directory by running cd -.
- LS_COLORS: This defines the color codes that are used to optionally add color output to the ls command. This is used to distinguish different file types and provide more information to the user at a glance.
- MAIL: The path to the current user’s mailbox.
- PATH: A list of directories that the system will check when searching for commands. When a user types a command, the system will check the directories in this order for the executable.
- LANG: The current language and localization settings, including character encoding.
- HOME: The home directory of the current user.
- _: The most recent command previously executed.
to these environment variables, some shell variables you will often see are
- BASHOPTS: The list of options that were used when bash was run. This can be useful for finding out if the shell environment will work the way you want.
- BASH_VERSION: The version of bash that is executed, in human-readable form.
- BASH_VERSINFO: The bash version, in machine-readable output.
- COLUMNS: The number of columns wide that are used to draw the output on the screen.
- DIRSTACK: The stack of directories that are available with the pushd and popd commands.
- HISTFILESIZE: Number of command history lines stored in a file.
- HISTSIZE: Number of command history lines allowed in memory.
- HOSTNAME: The host name of the computer at this time.
- IFS: The internal field separator to separate command line input. By default, this is a space.
- PS1: The main definition of the command prompt. This is used to define the appearance of the message when a shell session starts. The PS2 is used to declare secondary messages for when a command spans multiple lines.
- SHELLOPTS: Shell options that can be configured with the set option.
- UID: The UID of the current user.
Configuring shell and environment variables
To better understand the difference between shell and environment variables, and introduce the syntax for setting these variables, we’ll do a small demonstration
We’ll start by defining a shell variable within our current session. This is easy to achieve; We only need to specify a name and a value. We will adhere to the convention of keeping all caps for the variable name and setting it to a simple string.
- TEST_VAR=’Hello world!’
Here, we have used citations since the value of our variable contains a space. Also, we’ve used single quotes because the exclamation mark is a special character in the bash shell that typically expands to the bash history if it doesn’t escape or is enclosed in single quotes.
We now have a shell variable. This variable is available in our current session, but will not be passed on to child processes.
We can see this by grepping for our new variable within the set output:
- set | grep TEST_VAR
We can verify that this is not an environmental variable by trying the same with
- printenv | grep TEST_VAR
No output should be returned
Let’s take this as an opportunity to demonstrate a way to access the value of any shell or environment variable.
- echo $TEST_VAR
As you can see, reference the value of a variable by preceding it with a $ sign. The shell takes this to mean that it must substitute the value of the variable when it encounters this.
So now we have a shell variable. It should not be transmitted to any secondary process. We can generate a new bash shell from within our current one to demonstrate:
- echo $TEST_VAR
If we write bash to generate a child shell and then try to access the contents of the variable, nothing will be returned. This is what we expected.
Return to our
original shell by typing exit:
Creating environmental variables
Now, let’s convert our shell variable to an environment variable. We can do this by exporting the variable. The command to do this is appropriately named:
- export TEST_VAR
This will change our variable into an environment variable. We can check this by checking our environmental list again:
- printenv | grep TEST_VAR
This time, our variable appears. Let’s try our experiment with our child shell again:
- echo $TEST_VAR
Not bad! Our child shell has received the variable set by its parent. Before exiting this child shell, let’s try exporting another variable. We can set environmental variables in one step like this
: export NEW_VAR=”Testing export”
Test that is exported as an environment
- printenv | grep NEW_VAR
- =Testing export
Now, let’s go back to our original shell:
Let’s see if our new variable is available:
- echo $NEW_VAR
Nothing is returned.
This is because environmental variables are only passed on to secondary processes. There is no built-in way to set master shell environment variables. This is good in most cases and prevents programs from affecting the operating environment from which they were called.
The NEW_VAR variable was established as an environmental variable in our secondary shell. This variable would be available to itself and any of its shells and child processes. When we came out again to our main shell, that environment was destroyed.
Demote and unset variables
We still have our TEST_VAR variable defined as an environment variable. We can change it back to a shell variable by typing
- export -n
an environmental variable: printenv | grep TEST_VAR
However, it is still a shell variable:
- set | grep TEST_VAR
If we want to completely unset a variable, either shell or environmental, we can do it with the command
- unset TEST_VAR
We can verify that it is no longer configured:
- echo $TEST_VAR
Nothing is returned because the variable has been deconfigured
. Setting environmental variables
when logging in
We have already mentioned that many programs use environmental variables to decide the details of how to operate. We don’t want to have to set up important variables every time we start a new shell session, and we’ve already seen how many variables are already set when we log in, so how do we do and define variables automatically?
This is actually a more complex problem than it initially seems, due to the numerous configuration files that the bash shell reads depending on how it starts.
The difference between
login, non-started, interactive, and non-interactive
The bash shell reads different configuration files depending on how the session is started
One distinction between different sessions is whether the shell is generated as a login session or not. A shell login
is a shell session that begins by authenticating the user. If you are logging in to a terminal session or via SSH and authenticating, your shell session will be set as a login shell.
If you start a new shell session from
your authenticated session, as we did by calling the bash command from the terminal, a shell session is started without a login. You weren’t asked for your authentication details when you started your child shell.
Another distinction that can be made is whether a shell session is interactive or non-interactive.
An interactive shell session is a
shell session that is attached to a terminal. A non-interactive shell session is one that is not connected to a terminal session.
Therefore, each shell session is classified as login or no login and interactive or non-interactive.
A typical session that starts with SSH is usually an interactive login shell. A script run from the command line usually runs in a non-interactive shell and without login. A terminal session can be any combination of these two properties.
Whether a shell session is classified as a login shell
or not a login shell has implications for which files are read to initialize the shell
A session logged on as a login session will first read the configuration details from the /etc/profile file. Next, you’ll look for the first logon shell configuration file in the user’s home directory for user-specific configuration details.
It reads the first file it can find from ~/.bash_profile, ~/.bash_login, and ~/.profile and does not read any other files.
In contrast, a session defined as a shell with no login will read /etc/bash.bashrc and then the user-specific ~/.bashrc file to build its environment.
Non-interactive shells read the environment variable named BASH_ENV and read the specified file to define the new environment.
Implementation of environmental variables
As you can see, there are a variety of different files that we would normally have to look at to place our settings
This provides a lot of flexibility that can help in specific situations where we want certain settings in a login shell and other settings in a non-login shell. However, most of the time we will want the same configuration in both situations.
Fortunately, most Linux distributions configure login configuration files to obtain non-login configuration files. This means that you can define the environment variables you want within configuration files that do not log in. They will then be read in both scenarios.
, we’ll set user-specific environment variables, and we’ll usually want our settings to be available in both login shells and non-login shells. This means that the place to define these variables is in the ~/.bashrc file.
Open this file now:
- nano ~/.bashrc
Most likely, it already contains quite a bit of data. Most of the definitions here are for establishing bash options, which are not related to environmental variables. You can set environment variables as you would from the command line:
- export VARNAME=value
Any new environment variables can be added anywhere in the ~/.bashrc file, as long as they are not placed in the middle of another command or for loop. Then we can save and close the file. The next time you start a shell session, the environment variable declaration is read and transmitted to the shell environment. You can force your current session to read the file now by typing:
If you need to set variables for the entire system, you can think about adding them to /etc/profile, /etc/bash.bashrc, or /etc/environment.
Environmental and shell
variables are always present in your shell sessions and can be very useful. They are an interesting way for a parent process to set configuration details for their children and are a way to set options outside of files.
This has many advantages in specific situations. For example, some deployment mechanisms rely on environment variables to configure authentication information. This is useful because it doesn’t require keeping them in files that can be viewed by third parties.
There are many other, more mundane, but more common scenarios where you will need to read or alter your system environment. These tools and techniques should give you a good foundation for making these changes and using them correctly.