powershellenvironment-variables

How do I set an environment variable in PowerShell if it doesn't exist?


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?


Solution

  • 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 makes no distinction between $null and '', and either value results in undefining (removing) the target environment variable; 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.
    This design limitation of the .NET APIs - as of .NET 8 - has been reported in GitHub issue #95559

    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, you need to use the static SetEnvironmentVariable() method of the [System.Environment] class:

    # user environment
    [Environment]::SetEnvironmentVariable('FOO', 'bar', 'User')
    
    # system environment (requires 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.