December 13, 2019
About 5 minutes
Technology Cross-platform Shell Environment Variables

Variables in any environment

Using environment variables across platforms and languages

Contents

Environment variables are named values that can be read and acted on by a program but which are defined outside of it, before it runs. They are often used to configure details of a program’s behaviour that may be specific to a system or user, such as setting a default output directory. In build systems, they are often used to keep information such as API keys and certificate passwords private by keeping them out of the code repository. Environment variables exist in some form on virtually every modern platform, but the way you use them varies. To make using them easier, this page provides a quick cross-platform guide to common environment variable tasks.

The basics

This table compares how environment variables are used from different command line shells:

ActionGNU/Linux, BSD, macOSWindows cmdPowerShell1
Use a value$NAME%NAME%$Env:NAME
Set a valueexport NAME="Value"set NAME=Value$Env:NAME="Value"
Print a valueecho $NAME or
printenv NAME
echo %NAME% or
set NAME
$Env:NAME
List allprintenvsetls $Env:

1 PowerShell is primarily found on Windows, but it is cross-platform.

For example, if the environment variable TARGET refers to a target directory, you would cd $TARGET on macOS but cd %TARGET% on Windows.

Persisting variables

Normally an environment variable does not survive the shell or process that creates it, so you need to re-set any variables you use every session. You can get around this by setting the desired variables automatically whenever you start a terminal.

GNU/Linux, BSD

Edit the script that is run when you log in or start a terminal. The exact steps to do this can depend on several factors (see below), but the following will work most of the time:

  1. Edit the script that is run for new shells:
    nano ~/.bashrc
  2. Add lines to define the desired variables:
    export NAME="Value"
  3. Save the file and exit the editor:
    Ctrl + O, Ctrl + X

To define a variable for all users on the machine, sudo nano /etc/bash.bashrc instead.

Troubleshooting

There are many ways for the above to go wrong. First, the instructions assume you have nano installed. (It’s a common text file editor; you can substitute vi or emacs or whatever you have available.) Second, they assume you are using the Bash shell. (You probably are, but you can echo $SHELL to find out.) Third, Bash actually runs different startup script files depending on how the shell is started. If you are logging in (for example, via ssh), then Bash will look for ~/.bash_profile, ~.bash_login, or ~/.profile, and run the first one it finds. If you are not logging in (for example, opening a terminal window on your desktop), then it will look for ~/.bashrc instead. But, most ~/.bash_profile scripts check for and run ~/.bashrc if it exists, using ~/.bashrc usually works regardless. (As a final caveat, if the shell is non-interactive, that is, not tied to a terminal, Bash runs the script specified in the BASH_ENV environment variable!)

macOS

The basic idea is the same as for other Unix family systems. The file you edit depends on the version, though. (Starting in macOS 10.15, the default shell changed to zsh.)

Version 10.15+

  1. Edit the script that is run for new shells:
    nano ~/.zshrc
  2. Add lines to define the desired variables:
    export NAME="Value"
  3. Save the file and exit the editor:
    Ctrl + O, Ctrl + X

Older versions

  1. Edit the script that is run for new shells:
    nano ~/.bash_profile
  2. Add lines to define the desired variables:
    export NAME="Value"
  3. Save the file and exit the editor:
    Ctrl + O, Ctrl + X

Why ~/.bash_profile instead of ~/.bashrc? By default the Terminal app treats new shells as login shells, and the default ~/.bash_profile does not include ~/.bashrc. If you want to use ~/.bashrc for consistency, add the following to ~/.bash_profile:

if [ -r ~/.bashrc ]; then
source ~/.bashrc
fi

Windows

Environment variables for the current user (and the entire system) are usually edited from a control panel applet. You can bring it up as follows:

  1. Press Windows + I.
  2. In the Find a setting search field, type env.
  3. Choose Edit environment variables for your account.
  4. Use the dialog to add, delete, or edit variables.

You can also open this dialog from the command line:

rundll32 sysdm.cpl,EditEnvironmentVariables

With PowerShell, you can also persist an environment variable from the command line (this relies on the .NET class System.Environment):

[System.Environment]::SetEnvironmentVariable("NAME", "Value", [System.EnvironmentVariableTarget]::User)

Changing User to Machine would affect the entire system and not just your account, but requires appropriate privileges (for example, by using Run as Administrator to start the shell).

Access from programs

This section lists example snippets to get the value of an environment variable in many popular languages.

Environment variable names are case sensitive on most every platform except Windows. To avoid trouble, always treat them as if they were case sensitive.

C

#include <stdlib.h>
// ...

// NULL if variable is not defined
char* nameEnv = getenv("NAME");

C++

#include <cstdlib>
// ...

// NULL if variable is not defined
char* nameEnv = std::getenv("NAME");

C#

using System;
// ...

// null if variable is not defined
string nameEnv = Environment.GetEnvironmentVariable("NAME");

Java

// System.getenv() returns a Map<String, String> of all env vars
// null if variable is not defined
String nameEnv = System.getenv().get("NAME");

Node.js

// undefined if variable is not defined
let nameEnv = process.env.NAME;
// or
let nameEnv2 = process.env['NAME'];

PHP

// FALSE if variable is not defined
$nameEnv = getenv('NAME');

Python

import os
# ...

# None if variable is not defined
nameEnv = os.environ.get('NAME')
# or
nameEnv = os.getenv('NAME', 'default value')

Common environment variables

There are plenty more; these are some of the most useful in practice.

GNU/Linux, BSD, macOS

NamePurpose
PATHColon-separated list of directories to search for shell commands.
USERYour current username (e.g., jane).
HOMEPath of your home directory (e.g., /home/jane).
PWDPath of the current working directory.
SHELLPath to the shell you are using (e.g., /bin/bash).

Windows

NamePurpose
PATHSemicolon-separated list of directories to search for shell commands.
PATHEXTSemicolon-separated list of file extensions (including dot) considered to be executable as commands.
USERNAMEYour current username.
USERPROFILEPath of your home directory (e.g., C:\Users\jane).
ProgramFilesPath of the directory where programs are installed.
TEMPPath of the directory to use for temporary files.
AppDataPath of the directory where program settings and other per-user data should be stored, if that data should “roam” with the user to other machines.
LocalAppDataPath of the directory where per-user data that should not roam to other machines should be stored. (For example, very large files or files only valid for this machine.)

Have a comment or correction? Let me know! I don’t use an automated comment system on this site, but I do appreciate feedback and I update pages accordingly.