Manage the Windows PATH environment variable with PowerShell
Among many other tasks, IT teams can use PowerShell to manage and edit the PATH environment variable. Walk through these basic steps to access, add and remove system paths.
The Windows PATH environment variable is where applications look for executables -- meaning it can make or break a system or utility installation. Admins can use PowerShell to manage the PATH variable -- a process that entails string manipulation.
Everything covered in this article is applicable to both Windows PowerShell (5.1) and PowerShell (6+).
To access the PATH variable, use:
$env:Path
Depending on your system, the returned PATH variable string could be shorter or longer than the one in Figure 1. A semicolon separates each path in the list.
Splitting the string required the following command:
$env:Path -split ';'
This command in Figure 2 will return a string array of the path values that are a part of the PATH variable.
Add to the Windows PATH environment variable
To add to the PATH, append a semicolon and a new path on the end of the long path string. We can use PowerShell to check whether the path we want to add is already in the existing path.
First, pick a path to add:
$addPath = 'C:\TopSecret\Bin'
Path strings that refer to a directory are technically correct with or without a trailing slash -- '\' -- and, either way, that path will resolve correctly. To add a path to the PATH variable, first check whether the path is already there. To do so, check for the existence of the path both with and without the trailing slash.
Iterate through all the existing paths and use the following command to check if the new path is already included with or without a '\' on the end:
$regexAddPath = [regex]::Escape($addPath) $arrPath = $env:Path -split ';' | Where-Object {$_ -notMatch "^$regexAddPath\\?"}
Since we are using regular expressions to check for a trailing '\', escape the $addPath, as it will likely have slashes in it.
We use regular expressions here instead of the -notlike operator -- as in the example below -- because it would match on too many paths:
$arrPath = $env:Path -split ';' | Where-Object {$_ -notlike "$addPath*"}
For instance, using the output of my PATH variable as a reference, if we wanted to remove C:\WINDOWS and used the -notlike parameter, it would also remove C:\WINDOWS\system32 and others. Regular expressions let us explicitly target the path with or without a trailing slash.
After we have our array of paths that doesn't include our new one, we can add our path to it, join them with the semicolon and assign that back to the PATH variable:
$env:Path = ($arrPath + $addPath) -join ';'
We could then collect that array as a single function:
Function Add-PathVariable { param ( [string]$addPath ) if (Test-Path $addPath){ $regexAddPath = [regex]::Escape($addPath) $arrPath = $env:Path -split ';' | Where-Object {$_ -notMatch "^$regexAddPath\\?"} $env:Path = ($arrPath + $addPath) -join ';' } else { Throw "'$addPath' is not a valid path." } }
How to remove a path
It's just as simple to remove a path from the Windows PATH environment variable as it is to add one. For this example, circle back to Figure 2, which shows some of the paths on the computer, and remove Java:
$removePath = 'C:\Program Files (x86)\Common Files\Oracle\Java\javapath'
Again, split the PATH and only select the paths that don't match the $removePath with a possible trailing slash. This time, however, we will simply assign that output back to the PATH variable:
$regexRemovePath = [regex]::Escape($removePath) $arrPath = $env:Path -split ';' | Where-Object {$_ -notMatch "^$regexRemovePath\\?"} $env:Path = $arrPath -join ';'
Because this process is so similar to adding a path, we can combine these processes into one function rather than have two:
Function Set-PathVariable { param ( [string]$AddPath, [string]$RemovePath ) $regexPaths = @() if ($PSBoundParameters.Keys -contains 'AddPath'){ $regexPaths += [regex]::Escape($AddPath) } if ($PSBoundParameters.Keys -contains 'RemovePath'){ $regexPaths += [regex]::Escape($RemovePath) } $arrPath = $env:Path -split ';' foreach ($path in $regexPaths) { $arrPath = $arrPath | Where-Object {$_ -notMatch "^$path\\?"} } $env:Path = ($arrPath + $addPath) -join ';' }
Scopes
When you make changes to the PATH variable in PowerShell using the methods previously outlined, those changes are scoped only to the current PowerShell session. This means that if you close your console, your changes no longer exist. If you need the changes to be permanent, there are two ways to handle that, depending on how global you want the changes.
If you want to add a path to the PATH variable only for PowerShell and you want the change to be in PowerShell and only PowerShell, you can do this by adding a few lines to your PowerShell profile. To find your profile, simply type '$profile' into your PowerShell prompt.
For instance, if you place the Set-PathVariable function at the top of your profile.ps1, you can now add a path using the following:
Set-PathVariable AddPath 'C:\tmp\bin'
Now every time you launch PowerShell, it will add that path to your PATH variable.
The truly permanent and global way to edit the PATH variable is by using .NET with PowerShell launched with administrative privileges. The .NET framework has methods called GetEnvironmentVariable and SetEnvironmentVariable on the System.Environment class. The PATH variable using this method looks like the following:
[System.Environment]::GetEnvironmentVariable('PATH')
If you run the method without any parentheses or parameters, we can see all the overload definitions in Figure 3.
The second option lets us look at the variable value for different scopes. The wording used here is 'Target,' but it's easier to understand it as a scope. We know that System.EnvironmentVariableTarget is an enum by simply executing the class shown in Figure 4.
We can find all the possible values for it by using the System.Enum's GetValues method in Figure 5.
Now we can get and set environment variables for any of those three targets or scopes. When you edit the $env:PATH variable, you're editing the 'Process' scope of the variable, which is the default scope if you don't specify one.
To make an environment variable change permanent and global, we would edit the 'Machine' scope of the variable. This requires you to run PowerShell as an administrator.
To make the changes, we'll simply update the previous function to make it even more useful, as shown in Figure 6.
You'll notice that $Scope now has a ValidateSet block to ensure that only proper options are passed. It also has a default value of 'Process,' as this is the reasonable default used by .NET.
You can also access this script in my GitHub repository.
When you update a machine scope environment variable, other processes won't be aware of that change until they're restarted. It's usually good practice to reboot after you edit a machine scoped environment variable, especially if it's an urgent need.