45

I'm surprised that I didn't get the answer for this common scenario after searching the internet for a while.

How can I set an environment variable in PowerShell if it does not already exist?

0

3 Answers 3

91

The following code defines environment variable FOO for the current process, if it doesn't exist yet.

if ($null -eq $env:FOO) { $env:FOO = 'bar' }

# If you want to treat a *nonexistent* variable the same as
# an existent one whose value is the *empty string*, you can simplify to:
if (-not $env:FOO) { $env:FOO = 'bar' }

# Alternatively, via the Env: *drive*:
if (-not (Test-Path env:FOO)) { $env:FOO = 'bar' }

# Or even (quietly fails if the variable already exists):
New-Item -ErrorAction Ignore env:FOO -Value bar

In PowerShell (Core) 7.1+, which has null-coalescing operators, you can simplify to:

$env:FOO ??= 'bar'

Note:
Environment variables are strings by definition. If a given environment variable is defined, but has no value, its value is the empty string ('') rather than $null. Thus, comparing to $null can be used to distinguish between an undefined environment variable and one that is defined, but has no value.
However, note that assigning to environment variables in PowerShell / .NET prior to v7.5 / .NET 9 made no distinction between $null and '', and either value results in undefining (removing) the target environment variable (in v7.5+ / .NET 9+, assigning '' now creates an empty env. var.); similarly, in cmd.exe set FOO= results in removal/non-definition of variable FOO, and the GUI dialog (accessible via sysdm.cpl) doesn't allow you to define a variable with an empty string either. However, the Windows API (SetEnvironmentVariable) does permit creating environment variables that contain the empty string, albeit in-process only.
On Unix-like platforms, empty-string values are allowed too, and the native, POSIX-compatible shells (e.g, bash and /bin/sh) - unlike PowerShell - also allow you to create them (e.g, export FOO=). Note that environment variable definitions and lookups are case-sensitive on Unix, unlike on Windows.

Note: If the environment variable is created on demand by the assignment above ($env:FOO = ...), it will exist for the current process and any child processes it creates only Thanks, PetSerAl.


The following was mostly contributed by Ansgar Wiechers, with a supplement by Mathias R. Jessen:

On Windows[*], if you want to define an environment variable persistently, via the registry, you need to use the static SetEnvironmentVariable() method of the [System.Environment] class:

# user environment
[Environment]::SetEnvironmentVariable('FOO', 'bar', 'User')

# system environment (requires elevation, i.e. admin privileges)
[Environment]::SetEnvironmentVariable('FOO', 'bar', 'Machine')

Note that these definitions take effect in future sessions (processes), so in order to define the variable for the current process as well, run $env:FOO = 'bar' in addition, which is effectively the same as [Environment]::SetEnvironmentVariable('FOO', 'bar', 'Process').

When using [Environment]::SetEnvironmentVariable() with User or Machine, a WM_SETTINGCHANGE message is sent to other applications to notify them of the change (though few applications react to such notifications).
This doesn't apply when targeting Process (or when assigning to $env:FOO), because no other applications (processes) can see the variable anyway.

See also: Creating and Modifying Environment Variables (TechNet article).


[*] On Unix-like platforms, attempts to target the persistent scopes - User or Machine- are quietly ignored, as of .NET (Core) 7, and this non-support for defining persistent environment variables is unlikely to change, given the lack of a unified mechanism across Unix platforms.

Sign up to request clarification or add additional context in comments.

Comments

3

Code

function Set-LocalEnvironmentVariable {
    param (
        [Parameter()]
        [System.String]
        $Name,

        [Parameter()]
        [System.String]
        $Value,

        [Parameter()]
        [Switch]
        $Append
    )

    function Write-MyMessage {
        param(
            [System.String]
            $VarName
        )
        WWrite-Host "Local Environment variable " -ForegroundColor DarkYellow -NoNewline
        Write-Host "`"$VarName`"" -NoNewline -ForegroundColor Yellow
        Write-Host "  ➡  " -ForegroundColor DarkYellow -NoNewline
        try {
            Write-Host "`"$((Get-Item env:$VarName).Value)`"" -ForegroundColor Yellow
        }
        catch {
            Write-Host """""" -ForegroundColor Yellow
        }
    }

    if ($Append.IsPresent) {
        if (Test-Path "env:$Name") {
            $Value = (Get-Item "env:$Name").Value + $Value
        }
    }
    New-Item env:$Name -Value "$value" -Force | Out-Null
    if ($PSBoundParameters.Verbose.IsPresent) {
        Write-MyMessage -VarName $Name
    }
}

function Set-PersistentEnvironmentVariable {
    param (
        [Parameter()]
        [System.String]
        $Name,
    
        [Parameter()]
        [System.String]
        $Value,
    
        [Parameter()]
        [Switch]
        $Append        
    )
    
    function Write-MyMessage {
        param(
            [System.String]
            $VarName
        )
        Write-Host "Persistent Environment variable " -NoNewline -ForegroundColor DarkYellow
        Write-Host "`"$VarName`"" -NoNewline -ForegroundColor Yellow
        Write-Host "  ➡  " -NoNewline -ForegroundColor DarkYellow
        try {
            Write-Host "`"$((Get-Item env:$VarName).Value)`"" -ForegroundColor Yellow
        }
        catch {
            Write-Host """""" -ForegroundColor Yellow
        }
    }

    Set-LocalEnvironmentVariable -Name $Name -Value $Value -Append:$Append
    if ($Append.IsPresent) {
        $value = (Get-Item "env:$Name").Value
    }
    if ($IsWindows) {
        setx "$Name" "$Value" | Out-Null
        if ($PSBoundParameters.Verbose.IsPresent) {
            Write-MyMessage -VarName $Name
        }
        return
    }
    if ($IsLinux -or $IsMacOS) {
        $pattern = "\s*export\s+$name=[\w\W]*\w*\s+>\s*\/dev\/null\s+;\s*#\s*$Name\s*"
        $files = @("~/.bashrc", "~/.zshrc", "~/.bash_profile", "~/.zprofile")
        
        $files | ForEach-Object {
            if (Test-Path -Path $_ -PathType Leaf) {
                $content = [System.IO.File]::ReadAllText("$(Resolve-Path $_)")
                $content = [System.Text.RegularExpressions.Regex]::Replace($content, $pattern, [System.Environment]::NewLine);
                $content += [System.Environment]::NewLine + "export $Name=$Value > /dev/null ;  # $Name" + [System.Environment]::NewLine
                [System.IO.File]::WriteAllText("$(Resolve-Path $_)", $content)
            }
            
        }
        if ($PSBoundParameters.Verbose.IsPresent) {
            Write-MyMessage -VarName $Name
        }
        return
    }
    
    throw "Invalid platform."
}

  • function Set-PersistentEnvironmentVariable Set a variable/value in actual process and system. This function calls Set-LocalEnvironmentVariable function to set process scope variables and perform task for set variables in machine scope.

On Windows you can use:

On Linux we can add export VARIABLE_NAME=Variable value to file ~/.bash_profile. For a new bash terminal the process execute these instructions located in ~/.bash_profile.

On MacOS similiar to Linux but if you have zsh terminal the file is .zprofile, if the default terminal is bash, the file is .bash_profile. In my function code we need to add detection of default terminal if you wish. I assume that default terminal is zsh.

  • function Set-LocalEnvironmentVariable Set a variable/value in actual process. Using Drive env:.

Examples

#Set "Jo" value to variable "NameX", this value is accesible in current process and subprocesses, this value is accessible in new opened terminal.
Set-PersistentEnvironmentVariable -Name "NameX" -Value "Jo"; Write-Host $env:NameX

#Append value "ma" to current value of variable "NameX", this value is accesible in current process and subprocesses, this value is accessible in new opened terminal.
Set-PersistentEnvironmentVariable -Name "NameX" -Value "ma" -Append; Write-Host $env:NameX

#Set ".JomaProfile" value to variable "ProfileX", this value is accesible in current process/subprocess.
Set-LocalEnvironmentVariable "ProfileX" ".JomaProfile"; Write-Host $env:ProfileX



Output

Windows 10 Windows

Ubuntu WSL Ubuntu - Linux


References

Check About Environment Variables
Shell initialization files
ZSH: .zprofile, .zshrc, .zlogin - What goes where?

Comments

1

You can use the following code to set an environment variable in PowerShell if it doesn't exist:

if (!(Test-Path -Path Env:VAR_NAME)) {
    New-Item -Path Env:VAR_NAME -Value "VAR_VALUE"
}

Replace VAR_NAME with the name of the environment variable and VAR_VALUE with the desired value.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.