How To Read and Set Environmental and Shell Variables on Linux
Source: DigitalOcean
By Justin Ellingwood and Anish Singh Walia When interacting with your server through a shell session, there are many pieces of information that your shell compiles to determine its behavior and access to resources. Some of these settings are contained within configuration settings and others are determined by user input. One way that the shell keeps track of all of these settings and details is through an area it maintains called the environment. The environment is an area that the shell builds every time that it starts a session 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. Note: This tutorial has been validated on the latest Linux distributions including Ubuntu 22.04 LTS, Ubuntu 24.04 LTS, Debian 12, CentOS Stream 9, and Rocky Linux 9. All commands and examples work on modern Linux systems with Bash 5.x and later versions. If you’d like to follow along using your local system or a remote server, open a terminal and run the commands from this tutorial there. Before diving into the details, here are the essential points about environment and shell variables on Linux: Every time a shell session spawns, a process takes place to gather and compile information that should be available to the shell process and its child processes. It obtains the data for these settings from a variety of different files and settings on the system. The environment provides a medium through which the shell process can get or set settings and, in turn, pass these on to its child processes. The environment is implemented as strings that represent key-value pairs. If multiple values are passed, they are typically separated by colon (:) characters. Each pair will generally look something like this: If the value contains significant white-space, quotations are used: The keys in these scenarios are variables. They can be one of two types, environmental variables or shell variables. Environmental variables are variables that are defined for the current shell and are inherited by any child shells or processes. Environmental variables are used to pass information into processes that are spawned from the shell. This inheritance mechanism is fundamental to how Linux processes communicate configuration information. 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, like the current working directory. Unlike environment variables, shell variables are not automatically passed to child processes unless explicitly exported. For more information about how Bash handles variables and scripting, see our tutorial on What is Bash?. By convention, these types of variables are usually defined using all capital letters. This helps users distinguish environmental variables within other contexts. Each shell session keeps track of its own shell and environmental variables. We can access these in a few different ways. We can see a list of all of our environmental variables by using the env or printenv commands. In their default state, they should function exactly the same: Your shell environment may have more or fewer variables set, with different values than the following output: This is fairly typical of the output of both printenv and env. The difference between the two commands is only apparent in their more specific functionality. For instance, with printenv, you can request the values of individual variables: On the other hand, env lets you modify the environment that programs run in by passing a set of variable definitions into a command like this: Since, as we learned above, child processes typically inherit the environmental variables of 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 up through our system files and processes without our input. These show the environmental variables, but how do we see shell variables? The set command can be used for this. If we type set without any additional parameters, we will get a list of all shell variables, environmental variables, local variables, and shell functions: This is usually a huge list. You probably want to pipe it into a pager program to more easily deal with the amount of output: The amount of additional information that we receive back is a bit overwhelming. We probably do not need to know all of the bash functions that are defined, for instance. We can clean up the output by specifying that set should operate in POSIX mode, which won’t print the shell functions. We can execute this in a sub-shell so that it does not change our current environment: This will list all of the environmental and shell variables that are defined. We can attempt to compare this output with the output of the env or printenv commands to try to get a list of only shell variables, but this will be imperfect due to the different ways that these commands output information: This will likely still include a few environmental variables, due to the fact that the set command outputs quoted values, while the printenv and env commands do not quote the values of strings. 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 of setting persistent values for the session between processes, without writing changes to a file. Some environmental and shell variables are very useful and are referenced fairly often. The following table lists the most commonly used variables you’ll encounter: To better understand the difference between shell and environmental variables, and to introduce the syntax for setting these variables, we will do a small demonstration. We will begin by defining a shell variable within our current session. This is easy to accomplish; we only need to specify a name and a value. We’ll adhere to the convention of keeping all caps for the variable name, and set it to a simple string. Here, we’ve used quotations since the value of our variable contains a space. Furthermore, we’ve used single quotes because the exclamation point is a special character in the bash shell that normally expands to the bash history if it is not escaped or put into single quotes. We now have a shell variable. This variable is available in our current session, but will not be passed down to child processes. We can see this by grepping for our new variable within the set output: We can verify that this is not an environmental variable by trying the same thing with printenv: No output should be returned. Let’s take this as an opportunity to demonstrate a way of accessing the value of any shell or environmental variable. As you can see, reference the value of a variable by preceding it with a $ sign. The shell takes this to mean that it should substitute the value of the variable when it comes across this. So now we have a shell variable. It shouldn’t be passed on to any child processes. We can spawn a new bash shell from within our current one to demonstrate: If we type bash to spawn a child shell, and then try to access the contents of the variable, nothing will be returned. This is what we expected. Get back to our original shell by typing exit: Now, let’s turn our shell variable into an environmental variable. We can do this by exporting the variable. The command to do so is appropriately named: This will change our variable into an environmental variable. We can check this by checking our environmental listing again: This time, our variable shows up. Let’s try our experiment with our child shell again: Great! Our child shell has received the variable set by its parent. Before we exit this child shell, let’s try to export another variable. We can set environmental variables in a single step like this: Test that it’s exported as an environmental variable: Now, let’s exit back into our original shell: Let’s see if our new variable is available: This is because environmental variables are only passed to child processes. There isn’t a built-in way of setting environmental variables of the parent shell. This is good in most cases and prevents programs from affecting the operating environment from which they were called. The NEW_VAR variable was set as an environmental variable in our child shell. This variable would be available to itself and any of its child shells and processes. When we exited back into our main shell, that environment was destroyed. We still have our TEST_VAR variable defined as an environmental variable. We can change it back into a shell variable by typing: It is no longer an environmental variable: However, it is still a shell variable: If we want to completely unset a variable, either shell or environmental, we can do so with the unset command: We can verify that it is no longer set: Nothing is returned because the variable has been unset. We’ve already mentioned that many programs use environmental variables to decide the specifics of how to operate. We do not want to have to set important variables up every time we start a new shell session, and we have already seen how many variables are already set upon login, so how do we make 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 is started. The bash shell reads different configuration files depending on how the session is started. One distinction between different sessions is whether the shell is being spawned as a login or non-login session. A login shell is a shell session that begins by authenticating the user. If you are signing into a terminal session or through SSH and authenticate, your shell session will be set as a login shell. If you start a new shell session from within your authenticated session, like we did by calling the bash command from the terminal, a non-login shell session is started. You were not 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 is not attached to a terminal session. So each shell session is classified as either login or non-login and interactive or non-interactive. A normal session that begins with SSH is usually an interactive login shell. A script run from the command line is usually run in a non-interactive, non-login shell. A terminal session can be any combination of these two properties. Whether a shell session is classified as a login or non-login shell has implications on which files are read to initialize the shell session. A session started as a login session will read configuration details from the /etc/profile file first. It will then look for the first login shell configuration file in the user’s home directory to get user-specific configuration details. It reads the first file that it can find out of ~/.bash_profile, ~/.bash_login, and ~/.profile and does not read any further files. In contrast, a session defined as a non-login shell will read /etc/bash.bashrc and then the user-specific ~/.bashrc file to build its environment. Non-interactive shells read the environmental variable called BASH_ENV and read the file specified to define the new environment. This is typically set to ~/.bashrc. As you can see, there are a variety of different files that we would usually need to look at for placing 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 settings in both situations. Fortunately, most Linux distributions configure the login configuration files to source the non-login configuration files. This means that you can define environmental variables that you want in both inside the non-login configuration files. They will then be read in both scenarios. We will usually be setting user-specific environmental variables, and we usually will want our settings to be available in both login and non-login shells. This means that the place to define these variables is in the ~/.bashrc file. This will most likely contain quite a bit of data already. Most of the definitions here are for setting bash options, which are unrelated to environmental variables. You can set environmental variables just like you would from the command line: Any new environmental variables can be added anywhere in the ~/.bashrc file, as long as they aren’t placed in the middle of another command or for loop. We can then save and close the file. The next time you start a shell session, your environmental variable declaration will be read and passed on to the shell environment. You can force your current session to read the file now by typing: If you need to set system-wide variables, you may want to think about adding them to /etc/profile, /etc/bash.bashrc, or /etc/environment. System-wide variables affect all users on the system, so use caution when modifying these files. Environment variables can contain sensitive information like API keys, database passwords, and authentication tokens. Understanding security implications is crucial for protecting your system and applications. You can verify file permissions using the ls command with the -l flag: To set more restrictive permissions on your .bashrc file: This ensures only you can read and write the file. Understanding variable scope helps prevent accidental exposure of sensitive data. Shell variables are safer for temporary sensitive data because they’re not automatically inherited by child processes. Only export variables when child processes need access to them. When working with environment and shell variables, you may encounter several common issues. Here are solutions to the most frequent problems: If an environment variable isn’t available in a script, check the following: If variables disappear after closing the terminal: If commands aren’t found after adding directories to PATH: If a variable behaves unexpectedly: The env command is used to run a program in a modified environment or to display environment variables. When used with variable assignments, it sets variables only for that specific command execution: The export command, on the other hand, converts a shell variable into an environment variable, making it available to the current shell and all child processes: The key difference is that export makes the variable persistent in your shell session, while env sets it only for a single command execution. Environment variables may not be available in scripts for several reasons: To ensure variables are available in scripts, either export them in ~/.profile (for login shells) or explicitly source your configuration file in the script. To set environment variables for all users on the system, you have a few options: Important: Always use sudo when editing system-wide files, and be cautious as changes affect all users. Test changes in your user-specific files first. Environment variables can pose security risks if not managed properly: For sensitive data, consider using encrypted configuration files, credential managers, or secure key storage systems rather than plain environment variables. To remove an environment variable permanently, you need to: If the variable was set system-wide, you’ll need to edit the appropriate system file with sudo and restart your session for changes to take effect. Environmental and shell variables are always present in your shell sessions and can be very useful. They provide a way for a parent process to set configuration details for its children, and offer a method of setting options outside of files. This has many advantages in specific situations. For instance, some deployment mechanisms rely on environmental variables to configure authentication information. This is useful because it does not require keeping these in files that may be seen by outside parties. There are plenty of other common scenarios where you will need to read or alter the environment of your system. These tools and techniques should give you a good foundation for making these changes and using them correctly. Understanding variable scope, inheritance, and security implications helps you configure your Linux system effectively while maintaining good security practices. Now that you understand how to work with environment and shell variables, you can apply this knowledge to more advanced shell scripting tasks. Consider exploring these related tutorials: To practice working with environment variables in a real Linux environment, deploy a DigitalOcean Droplet and experiment with setting variables for your applications and scripts. DigitalOcean Droplets provide a clean Linux environment where you can safely test configuration changes without affecting your local system. Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases. Learn more about our products Former Senior Technical Writer at DigitalOcean, specializing in DevOps topics across multiple Linux distributions, including Ubuntu 18.04, 20.04, 22.04, as well as Debian 10 and 11. I help Businesses scale with AI x SEO x (authentic) Content that revives traffic and keeps leads flowing | 3,000,000+ Average monthly readers on Medium | Sr Technical Writer @ DigitalOcean | Ex-Cloud Consultant @ AMEX | Ex-Site Reliability Engineer(DevOps)@Nutanix This textbox defaults to using Markdown to format your answer. You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link! I think you addressed this in the beginning of the tutorial, but is it possible to set a shell/environment variable with two different arguments? For instance, I have an executable named “st” and another named “st_mona” and I want to set an environment variable inside the Makefile to both. The code right now is: But (if I read your article correctly) it seems to me that if this is possible it should instead read: Is this right or entirely not possible? @sgarrett: It’s important to remember that Makefiles are not actually shell scripts. There share a number of things, but Make is it’s own beast. The way you’re doing it is correct for make. You can check out the documentation for Make here: http://www.gnu.org/software/make/manual/html_node/ Though it’s a bit overwhelming. Personally I always learn better by example. I’d suggest browsing around GitHub and looking at other project’s Makefiles. Ah, thank you so much! That really helps :) Nice tutorial…I really appreciate your effort! Is it ~/.bash.rc or ~/.bashrc? Is there a file such as /etc/baash.bashrc? I can’t find /etc/bash.bashrc as well on my CoreOS machine This was a very useful and well written tutorial, thank you! Great tutorial. Certainly will be my refrence. thanks the author alot! :) Nicely explained, gives me a better understanding. Thanks Please complete your information! Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation. Full documentation for every DigitalOcean product. The Wave has everything you need to know about building a business, from raising funding to marketing your product. Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter. New accounts only. By submitting your email you agree to our Privacy Policy Scale up as you grow — whether you're running one virtual machine or ten thousand. Sign up and get $200 in credit for your first 60 days with DigitalOcean.* *This promotional offer applies to new accounts only.