Use PowerShell to Modify Your Environmental Path


Summary: Learn how to use Windows PowerShell to work with your environmental path variable.

 

Weekend Scripter: Use Windows PowerShell to Modify Your Path


Microsoft Scripting Guy Ed Wilson here. Welcome back to the weekend and Guest Blogger Sean Kearney. Read more about Sean and his previous guest blog posts. Now without further ado (or RDO or CDO), here is Sean.

 

Back in the good old days, we had a command called path in MS-DOS. It was used and abused heavily because that’s how you had to make things work. Either dump the application into a common folder or take its folder and do something like this:

PATH %PATH%;C:THIS;

This would allow you to now just run the application that was located in the this folder by just typing its name rather than adding an explicit path every time.

This is still available to us of course, but it is isolated to the current session and is only temporary. However, in the Land of Windows PowerShell, there is still a need to modify PATH. You may have a common script folder or an added console application folder (such as C:Sysinternals)

For whatever reason, if you want to modify the path, accessing the data in the PATH variable is easy, but making a modification can be confusing. Windows PowerShell provides natively a one-way path to read the system environment variables using $ENV

$ENV:PATH

But try as you might, there is no built-in Set-Path cmdlet or even an Append-Path (of course Append is not an approved verb) or Find-Path-to-the-Yellow-Brick-Road.

Of course, I’m not sure that last one would have been useful for anybody other than Dorothy or Toto.

But there is no obvious way to do it. Even running CMD.EXE as an administrator only makes the change temporary. So how do we gain this capability?

We leverage the registry with Windows Powershell. The environment variables are all stored under:

HKEY_LOCAL_MACHINESystemCurrentControlSetControlSession ManagerEnvironment

Therefore, it just becomes a matter of editing the key. Now keep in mind this is a permanent and global change. You will need to be running Windows PowerShell as an administrator.

First, to read the key with the content, you need to use the following command:

Get-ItemProperty -Path ‘Registry::HKEY_LOCAL_MACHINESystemCurrentControlSetControlSession ManagerEnvironment’ -Name PATH

The previous command will dump four separate properties on the screen—the PSPath, PSParentPath, PSChildname, PSProvider—and the actual ItemProperty path. We just want the path so that we can modify the previous command to look like the following:

(Get-ItemProperty -Path ‘Registry::HKEY_LOCAL_MACHINESystemCurrentControlSetControlSession ManagerEnvironment’ -Name PATH).Path

Now if we would like to add to that property, all we need to do is concatenate to the current value and set it back in the registry:

$oldPath=(Get-ItemProperty -Path ‘Registry::HKEY_LOCAL_MACHINESystemCurrentControlSetControlSession ManagerEnvironment’ -Name PATH).Path

$newPath=$oldPath+’;C:NewFolderToAddToTheList’

Set-ItemProperty -Path ‘Registry::HKEY_LOCAL_MACHINESystemCurrentControlSetControlSession ManagerEnvironment’ -Name PATH –Value $newPath

Now you’ll have to restart Windows PowerShell to see the change, but now all applications (including Windows) will have access to the updated path. But wouldn’t it make more sense to make these into cmdlets or advanced functions? I would think so. So we can just use $ENV:PATH to read it, so no sense rebuilding the wheel. I just need to easily add to the path. But we can get fancier than the old path command from DOS. We can add in some error checking, like validating if it’s already in the path or if the folder even exists.

Function global:ADD-PATH()
{
[Cmdletbinding()]
param
(
[parameter(Mandatory=$True,
ValueFromPipeline=$True,
Position=0)]
[String[]]$AddedFolder
)

# Get the current search path from the environment keys in the registry.

$OldPath=(Get-ItemProperty -Path ‘Registry::HKEY_LOCAL_MACHINESystemCurrentControlSetControlSession ManagerEnvironment’ -Name PATH).Path

# See if a new folder has been supplied.

IF (!$AddedFolder)
{ Return ‘No Folder Supplied. $ENV:PATH Unchanged’}

# See if the new folder exists on the file system.

IF (!(TEST-PATH $AddedFolder))
{ Return ‘Folder Does not Exist, Cannot be added to $ENV:PATH’ }

# See if the new Folder is already in the path.

IF ($ENV:PATH | Select-String -SimpleMatch $AddedFolder)
{ Return ‘Folder already within $ENV:PATH’ }

# Set the New Path

$NewPath=$OldPath+’;’+$AddedFolder

Set-ItemProperty -Path ‘Registry::HKEY_LOCAL_MACHINESystemCurrentControlSetControlSession ManagerEnvironment’ -Name PATH –Value $newPath

# Show our results back to the world

Return $NewPath
}

There you go: a nice new feature to add to your Windows PowerShell arsenal. If you want to get fancy and have a Get-Path, an alias should have done nicely for that, but it seems to hit the 260-character limit for some reason in the path. Therefore, we will cheat with this one-line function add-on:

FUNCTION GLOBAL:GET-PATH() { Return $ENV:PATH }

We can get even fancier now and have the ability to remove items from the path with a little magic from the –replace operator

Function global:REMOVE-PATH()
{
[Cmdletbinding()]
param
(
[parameter(Mandatory=$True,
ValueFromPipeline=$True,
Position=0)]
[String[]]$RemovedFolder
)

# Get the Current Search Path from the environment keys in the registry

$NewPath=(Get-ItemProperty -Path ‘Registry::HKEY_LOCAL_MACHINESystemCurrentControlSetControlSession ManagerEnvironment’ -Name PATH).Path

# Find the value to remove, replace it with $NULL. If it’s not found, nothing will change.

$NewPath=$NewPath –replace $RemovedFolder,$NULL

# Update the Environment Path

Set-ItemProperty -Path ‘Registry::HKEY_LOCAL_MACHINESystemCurrentControlSetControlSession ManagerEnvironment’ -Name PATH –Value $newPath

# Show what we just did

Return $NewPath

}

Remember you will have to reload Windows PowerShell to get the new changes, and these changes are permanent. So be careful! To simplify getting the code, I put the functions into a module, and uploaded it to the Scripting Center Script Repository.

 

Thank you, Sean, for providing an great article and module. Please join us tomorrow for more Windows PowerShell goodies from Sean.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

 

 

Comments (16)

  1. reidca says:

    I found the code in this article did not work.

    The folder was getting added to the registry key however it was not being shown in the path environment variable.

    This could be seen by examining the PATH at the command prompt echo %PATH% or in Powershell $env:Path.

    Using the [System.Environment]::SetEnvironmentVariable method works fine.

    Modified function

    Function Add-FolderToPath()
    {
    [CmdletBinding(PositionalBinding=$false)]
    param(
    [parameter(Mandatory=$True,ValueFromPipeline=$True)] [ValidateScript({Test-Path -Path $_ -PathType Container})] [String] $Folder
    )

    Write-Verbose "Adding folder ""$Folder"" to SYSTEM path"

    # Get the current search path from the environment keys in the registry.
    $OldPath = [System.Environment]::GetEnvironmentVariable("path")

    # See if the new Folder is already in the path.
    IF ($OldPath | Select-String -SimpleMatch $Folder)
    {
    Write-Warning "Folder ""$Folder"" is already in the path"
    }
    else
    {
    # Set the New Path
    $NewPath = $OldPath+ ’;’ + $Folder

    [System.Environment]::SetEnvironmentVariable("path",$NewPath)

    # Show our results back to the world
    Return $NewPath
    }

  2. jrv says:

    Sometimes we may just need to temporarily add to the path. This can be done easily:

    $env:path+='c:this'

    We can use string editing methods to remove an item:

    $env:path=$env:path.Replace('c:this','')

    We could eventokenize the path and sort its order or insert into the beginning.

    The path is changed for the current PowerShell session only.

    1. suv3ndu says:

      Thanks! I was looking for this.
      One small correction, it will be $env:path+=’;c:this’
      Without semicolon, new location will to add properly.

      P.S:Another funny thing I noticed…this site is showing your reply was posted 46yrs ago 🙂

  3. reidca says:

    Please note there is a bug in the code I posted before – this function only changes the path for the current process. You need to specify the machine as a target as per this code:

    Function Add-FolderToPath()
    {
    [CmdletBinding(PositionalBinding=$false)]
    param(
    [parameter(Mandatory=$True,ValueFromPipeline=$True)] [ValidateScript({Test-Path -Path $_ -PathType Container})] [String] $Folder
    )

    Write-Verbose "Adding folder ""$Folder"" to SYSTEM path"

    # Get the current search path from the environment keys in the registry.
    $OldPath = [System.Environment]::GetEnvironmentVariable("path")

    # See if the new Folder is already in the path.
    IF ($OldPath | Select-String -SimpleMatch $Folder)
    {
    Write-Warning "Folder ""$Folder"" is already in the path"
    }
    else
    {
    # Set the New Path
    $NewPath = $OldPath+ ’;’ + $Folder

    [System.Environment]::SetEnvironmentVariable("path",$NewPath,’Machine’)

    # Show our results back to the world
    Return $NewPath
    }

  4. mjocasio23 says:

    If decide to use the .NET method to change the environment variables……. Will this also change the registry or this is only a Powrshell session change that it will revoke after the session is destroy.

    This is the methonds

    [Environment]::SetEnvironmentVariable("TestVariable",$null,"Machine")

  5. jrv says:

    Kuzun – good catch.

    For everyone else this too: (Process only)

    [Environment]::SetEnvironmentVariable("HOTSOUP","Vichyssoise")

    Sean – you could add switches to your path library to cause it to update the user and machine settings.

  6. Kazun says:

    Great posts Sean!

    As another variant:

    [Environment]::SetEnvironmentVariable("Path",$Env:Path + ";C:SysinternalsSuiteset", "Machine")

    To jrv:

    $env:path+=';c:this'

  7. Sean Kearney says:

    @jrv and @kazun

    Whoa!  More great ideas ! Love it!  Actually the one thing I should add (since it can CHANGE something is our good friend -whatif )

    Sean

  8. Sean Kearney says:

    There's another little bonus here you may not have caught.    It's also pretty easy with a Split() and Sort-Object to find duplicate references to paths and null points.  (Believe it or not, if you install enough stuff, you get them.)

    Try doing a (GET-PATH).split(";") | SORT-OBJECT and you'll probably see what I mean.  I actually had THREE references to the Powershell folder created by different applications and 4 path entries (Two semicolons side by side)

    I'm not certain it made anything faster but I felt better with a clean path 🙂

  9. Klaus Schulte says:

    Nice to have you back here, Sean!

    The good old path variable is really something that could be well wrapped into some Powershell functions!

    @jrv, katzun: Definitely right! We can use the .net framework "Environment" class to make permanent changes and we can use PS build in ability to manage temporary environment variables!

    I personally need only temporary changes to the path variable during the run of scripts, mostly to add some special versions of java to the beginning of the path!

    Long ago 🙂 … I really have changed the global path variable frequently.

    Nowadays I do, what Sean mentioned before, change it after some installations that ruined the path or duplicated the entries manually.

    Klaus.

  10. further ado says:

    If only there was a <rimshot /> tag to put after "without further ado (or RDO or CDO)".  Love it!

  11. Erkki says:

    If you leave a backslash as the last character, like in the text above

    "$newPath=$oldPath+’;C:NewFolderToAddToTheList’",

    the path will brake if you add something after that e.q. ";c:myfolder" .

  12. Kelly Brownsberger says:

    Not sure about the methods prescribed in this post, but as one of the comments suggested… the following powershell worked just fine for me (immediately shows up in System -> Environment Variables -> Path (Machine)

    [Environment]::SetEnvironmentVariable("Path",$Env:Path + ";C:SysinternalsSuiteset", "Machine")

  13. Geo says:

    Great stuff! I would like to make one correction, however. The claim that changes to the path in the Windows Console are only temporary is false. You’ve forgotten about the SETX command! Yes, "SET PATH" only affects the current session, but SETX is permanent.
    Furthermore, "SETX /M" make changes to the Administrator/Machine PATH variable.

    Now, while it’s true that you can access the Windows commands in PowerShell, that’s not why we’re here! If so, we could just write a batch script and be done with it! 🙂

    I simply wanted to point out that the statement that changes made in the WINDOWS console are temporary is incorrect.

    Thanks for a great post!

  14. alrogg says:

    careful here!

    there are 2 registry keys for the path variable:

    A) the general path variable in
    HKEY_LOCAL_MACHINESystemCurrentControlSetControlSession ManagerEnvironment
    B) the user path variable in
    HKEY_CURRENT_USEREnvironment

    your function

    FUNCTION GLOBAL:GET-PATH() { Return $ENV:PATH }

    returns A merged with B (it’s what $ENV:PATH does)

    but your function

    Function global:REMOVE-PATH()

    handles only A.

    Inconsistency really can ruin an API. So I would change that.

  15. Robert says:

    This is simple and works great and will be persistent across sessions. To have this persistent for only a user, change ‘machine’ to ‘user’.

    $CurrentValue = [Environment]::GetEnvironmentVariable(“PSModulePath”, “Machine”)
    [Environment]::SetEnvironmentVariable(“PSModulePath”, $CurrentValue + “;C:\Program Files\Fabrikam\Modules”, “Machine”)

Skip to main content