714

I have found out that setting the PATH environment variable affects only the old command prompt. PowerShell seems to have different environment settings. How do I change the environment variables for PowerShell (v1)?

Note:

I want to make my changes permanent, so I don't have to set it every time I run PowerShell. Does PowerShell have a profile file? Something like Bash profile on Unix?

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Vasil
  • 32,548
  • 26
  • 84
  • 113
  • 1
    I'd like to have a central profile located on a file share. Synchronization is a pain. Creating a stub profile with . \\computer\share\path\Profile.ps1 seems like a kludge (try Notepad $Profile). It would be nice if there was a way to permanently change the $Profile automatic variable. – Nathan Hartley May 05 '11 at 14:09
  • 5
    No the PATH environment *does* affect powershell command prompt as well. What differs though is that powershell does not accept paths enclosed in quotes. Solution: remove all enclosing quotes (`"`) in the path environment variable – Nilzor Apr 16 '13 at 11:12
  • 3
    IF YOU LAND HERE FOR PS > v1... Further to Nilzor's comment above: Use this to remove all " from paths in the PATH environment variable for your session: `$($Env:PATH).Split(';') | %{ $str += "$($_.Trim('"'));" }; $Env:PATH=$str` – d3r3kk Aug 19 '14 at 22:51

18 Answers18

661

If, some time during a PowerShell session, you need to append to the PATH environment variable temporarily, you can do it this way:

$env:Path += ";C:\Program Files\GnuWin32\bin"
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
mloskot
  • 33,165
  • 10
  • 97
  • 122
  • 4
    +1 :: This one-liner is quite effective for session-based invocations as with mingw ... I.E. $env:PATH += ";c:\MinGW\msys\1.0\bin" ^ {some mingw bin ... } – Edward J Beckett Feb 08 '13 at 19:10
  • 2
    and how do I remove a path? – becko Feb 06 '14 at 14:14
  • @becko ask new SO question. This is specifically about *setting* env variable. – mloskot Feb 06 '14 at 20:21
  • 19
    If you need your path to be called before standard one, insert it at the beginning `$env:Path = "C:\MyPath;$env:Path"` – Michael Freidgeim Feb 11 '17 at 10:57
  • 4
    ****Don't forget the semicolon at the start of the appending string, as seen in @Kevin 's comment. This is pretty obvious, but can be missed if you simply copy/paste the code in the answer and didn't have a semicolon at the end of the existing path. I'll try to submit an edit. – Matt Goodrich Jun 18 '17 at 06:13
  • 1
    @MattGoodrich I've rollback to previous revision – Cœur Jun 18 '17 at 10:59
  • becko, to remove a path requires some code: $env:PATH = ($env:PATH -split ";" | %{ if($_ -ne "/path_to_remove") { $_ } }) -join ";" – karezza Jun 13 '18 at 22:02
621

Changing the actual environment variables can be done by using the env: namespace / drive information. For example, this code will update the path environment variable:

$env:Path = "SomeRandomPath";             (replaces existing path) 
$env:Path += ";SomeRandomPath"            (appends to existing path)

There are ways to make environment settings permanent, but if you are only using them from PowerShell, it's probably a lot better to use your profile to initiate the settings. On startup, PowerShell will run any .ps1 files it finds in the WindowsPowerShell directory under My Documents folder. Typically you have a profile.ps1 file already there. The path on my computer is

C:\Users\JaredPar\Documents\WindowsPowerShell\profile.ps1
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
JaredPar
  • 673,544
  • 139
  • 1,186
  • 1,421
  • 45
    $profile is an automatic variable that points at your user profile for all PowerShell hosts. – JasonMArcher Apr 03 '09 at 22:31
  • 16
    Note that (split-path $profile)(to get the containing folder) can contain multiple profile files: profile.ps1 should be loaded by all hosts, _profile.ps1 just by the specified host. For PowerShell.exe (console host), this is Microsoft.PowerShell_profile.ps1. – Richard Apr 04 '09 at 14:44
  • 11
    What if I don't have a folder WindowsPowerShell in my documents? Should I create the folder and the file? What should I put in the file if I want to add `C:\path\to\file.ext` to the environment variables? EDIT: found it already. Answer is yes, create it. The file should consist of 1 line: `$env:path += ;C:\path\to\file.ext"`. – Lewistrick Mar 11 '15 at 10:34
  • 8
    @Lewistrick You don't have a profile by default. I followed these instructions to create one: http://www.howtogeek.com/50236/customizing-your-powershell-profile/ – MikeB Oct 13 '15 at 00:45
  • 17
    Be careful doing this -- it will clobber your existing path. ```$env:Path = "SomeRandomPath"; ``` Instead - see @mloskot, below. – John Mark Nov 24 '15 at 00:16
  • 1
    `;` is essential to prepend to the value string as the delimiter to separate pathes –  Jun 17 '17 at 16:55
  • I've got a text file will all the variables needed for Visual Studio 2017 build tools to run, is this the proper way to add those variables in? the `PATH` variable has some stuff added on, would your solution wipe out everything originally in the `PATH` variable? I'd rather not have to edit the variables myself, but my batch file just won't open PS after initialising the environment. – Ungeheuer Jul 13 '17 at 01:47
  • Can I add individual Variables (like $timo='123') to the profile file or is it better do have a `.psd1` like [here](https://stackoverflow.com/a/54706752/1705829)? – Timo Oct 24 '19 at 09:41
  • 2
    I don't see why this is considered an answer. The question is how to permanently update the PATH, and this does not do that. This is *not* an answer to the question asked. "I want to make my changes permanent, so I don't have to set it every time I run PowerShell." is NOT answered. (I have added an answer that affects a permanent change on this thread). – YorSubs Mar 11 '20 at 12:57
  • @YorSubs The key requirement is "so I don't have to set it every time", I think the client doesn't always know the best way to get what they want so I think we should look past the solution proposed by the client and give the best solution that meets their needs. (NB: client also proposed a "profile file") – Myster Jun 24 '20 at 23:16
332

You can also modify user/system environment variables permanently (i.e. will be persistent across shell restarts) with the following:

Modify a system environment variable

[Environment]::SetEnvironmentVariable
     ("Path", $env:Path, [System.EnvironmentVariableTarget]::Machine)

Modify a user environment variable

[Environment]::SetEnvironmentVariable
     ("INCLUDE", $env:INCLUDE, [System.EnvironmentVariableTarget]::User)

Usage from comments - add to the system environment variable

[Environment]::SetEnvironmentVariable(
    "Path",
    [Environment]::GetEnvironmentVariable("Path", [EnvironmentVariableTarget]::Machine) + ";C:\bin",
    [EnvironmentVariableTarget]::Machine)

String based solution is also possible if you don't want to write types

[Environment]::SetEnvironmentVariable("Path", $env:Path + ";C:\bin", "Machine")
Giulio Caccin
  • 2,554
  • 6
  • 30
  • 47
hoge
  • 3,349
  • 1
  • 13
  • 2
  • 7
    This is a very useful for restricted access systems. – h0tw1r3 Sep 21 '12 at 17:57
  • 17
    @AndresRiofrio, Yes, this is permanent. Usage: `[Environment]::SetEnvironmentVariable("Path", $env:Path + ";C:\bin", [EnvironmentVariableTartget::Machine)` **You will not see the result of this change until you start a new powershell session.** That is, if you inspect $env:Path immediately after running this command, you will see what $env:Path was before the command. To update, close and open the shell or start a new session. – FLGMwt Mar 26 '14 at 18:47
  • 7
    @FLGMwt you have a typo, correct is: [Environment]::SetEnvironmentVariable("Path", $env:Path + ";C:\bin", [EnvironmentVariableTarget]::Machine) – enthus1ast Jul 21 '14 at 11:18
  • 11
    You can write the string "Machine" or "User" instead of the whole .NET enum. From [Technet](http://technet.microsoft.com/en-us/library/ff730964.aspx). – bouvierr Aug 06 '14 at 22:35
  • 1
    @FLGMwt Is there any way to "refresh" the session to see said changes without closing/opening the shell? – Erutan409 Jan 20 '16 at 16:07
  • 2
    @Erutan409 There's three targets possible for SetEnvironmentVariable. `[EnvironmentVariableTarget]::Machine` which affects new processes for everybody permanently, `[EnvironmentVariableTarget]::User` which affects new processes for the current user permanently, and `[EnvironmentVariableTarget]::Process`, which affects the current process environment only. You could set the variable twice, once for the User/Machine, and once for the Process. That would affect the variable for new processes and the current process. Other current processes wouldn't be affected, however. – Bacon Bits Jan 22 '16 at 18:52
  • Alternatively you can use `setx`, but also this requires a shell restart. So @BaconBits has the best answer. Which should be added to the answer above. – CMCDragonkai Apr 06 '16 at 07:05
  • 2
    This doesn't seem to work for me. I tried to add a directory to the User path variable, and everything seemed to work, but after starting a new Powershell terminal `$env::PATH` remained unchanged. – Ajedi32 Jul 19 '16 at 16:26
  • 1
    This answer should be modified to demonstrate the usage as @FLGMwt is showing. – codea Oct 28 '16 at 14:14
  • 6
    I think that answer should also demonstrate usage for setting user variable, like this `[Environment]::SetEnvironmentVariable("Path", [Environment]::GetEnvironmentVariable("Path", "User") + ";C:\bin", "User")` – Saito Feb 01 '17 at 14:31
  • 4
    I would like you to be aware of the fact that this answer will ruin any dynamic (a.k.a expanded) items in your path. Eg. if you have `GIT_INSTALL_ROOT=c:\tools\git` and `PATH=...;%GIT_INSTALL_ROOT%\cmd;%GIT_INSTALL_ROOT%\mingw64\bin` in your Environment you will lose all references to `GIT_INSTALL_ROOT` variable in your Path, so it will become hardwired to some fixed directory – maoizm Jul 10 '17 at 01:28
  • Again, this is NOT an answer to the question "I want to make my changes permanent, so I don't have to set it every time I run PowerShell.". It is NOT in any way permanent (I have added an answer that affects a permanent change on this thread). – YorSubs Mar 11 '20 at 12:58
  • 1
    This is a good solution, just make sure that concatenating the existing `$env:PATH` won't add stuff you didn't mean to. It's pretty common to modify `PATH` in initialisation scripts and you may not be aware that it's been altered for that specific session. For instance, Powershell 7.0.2 adds itself to `PATH` upon session initialise, so this technique will add that temporary entry every time you call `SetEnvironmentVariable()`. This is just a heads up, as I do think this is the right way of updating the `PATH` permanently. – Christiano Kiss Nov 16 '20 at 11:56
  • >>>`refreshenv` – Hicsy Feb 01 '21 at 00:55
  • @Saito your command does not `set` the `env var` but `add` to the `env var`. This is a difference. – Timo Mar 28 '21 at 07:04
  • in `pwsh7`, you see the **change** immediately **without a shell restart**, `refreshenv` here**not necessary**. But the path does not work, i.e. you cannot call a file from the new path without the full path name. This works with a new shell. – Timo Mar 28 '21 at 07:07
71

From the PowerShell prompt:

setx PATH "$env:path;\the\directory\to\add" -m

You should then see the text:

SUCCESS: Specified value was saved.

Restart your session, and the variable will be available. setx can also be used to set arbitrary variables. Type setx /? at the prompt for documentation.

Before messing with your path in this way, make sure that you save a copy of your existing path by doing $env:path >> a.out in a PowerShell prompt.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
tjb
  • 10,430
  • 7
  • 61
  • 85
  • 5
    Seems to only work when 'running as administrator', and thereafter takes effect only for 'running as administrator' PowerShell consoles, not regularly running ones. – matanster Feb 21 '13 at 12:20
  • 1
    Here is some [official Microsoft documentation for Setx](http://technet.microsoft.com/en-us/library/cc755104(v=ws.10).aspx). –  May 31 '13 at 21:03
  • 14
    Ouch - just got hit by the 1024 character limit of setx; thankfully I followed the advice to record the existing value of $end:Path. Just something to be aware of: http://superuser.com/questions/387619/overcoming-the-1024-character-limit-with-setx – Jonno Aug 14 '13 at 11:49
  • 4
    Why not set `$env:PATH` first, then `setx /m PATH "$env:PATH"` so that it applies locally and globally without shell restart? – tresf Feb 09 '19 at 20:40
  • 2
    Nice! Although setx is not a native cmdlet, still a much better and easily forgotten alternative to those obnoxious long winded .NET Framework calls! It's baffling that even Powershell 7 still doesn't come with an official cmdlet to persist envvars though. What. Feels like a feature that should have parity with 'ls'. – Jonas Mar 14 '20 at 14:15
  • @matanster -m specifies 'Machine' mode, if you want to set user environment variables, just leave away the -m (or /M) – mgross Jan 08 '21 at 09:56
34

Like JeanT's answer, I wanted an abstraction around adding to the path. Unlike JeanT's answer I needed it to run without user interaction. Other behavior I was looking for:

  • Updates $env:Path so the change takes effect in the current session
  • Persists the environment variable change for future sessions
  • Doesn't add a duplicate path when the same path already exists

In case it's useful, here it is:

function Add-EnvPath {
    param(
        [Parameter(Mandatory=$true)]
        [string] $Path,

        [ValidateSet('Machine', 'User', 'Session')]
        [string] $Container = 'Session'
    )

    if ($Container -ne 'Session') {
        $containerMapping = @{
            Machine = [EnvironmentVariableTarget]::Machine
            User = [EnvironmentVariableTarget]::User
        }
        $containerType = $containerMapping[$Container]

        $persistedPaths = [Environment]::GetEnvironmentVariable('Path', $containerType) -split ';'
        if ($persistedPaths -notcontains $Path) {
            $persistedPaths = $persistedPaths + $Path | where { $_ }
            [Environment]::SetEnvironmentVariable('Path', $persistedPaths -join ';', $containerType)
        }
    }

    $envPaths = $env:Path -split ';'
    if ($envPaths -notcontains $Path) {
        $envPaths = $envPaths + $Path | where { $_ }
        $env:Path = $envPaths -join ';'
    }
}

Check out my gist for the corresponding Remove-EnvPath function.

Community
  • 1
  • 1
Michael Kropat
  • 12,704
  • 9
  • 64
  • 85
17

Although the current accepted answer works in the sense that the path variable gets permanently updated from the context of PowerShell, it doesn't actually update the environment variable stored in the Windows registry.

To achieve that, you can obviously use PowerShell as well:

$oldPath=(Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH).Path

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

Set-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH –Value $newPath

More information is in blog post Use PowerShell to Modify Your Environmental Path

If you use PowerShell community extensions, the proper command to add a path to the environment variable path is:

Add-PathVariable "C:\NewFolderToAddToTheList" -Target Machine
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
gijswijs
  • 1,235
  • 13
  • 19
14

All the answers suggesting a permanent change have the same problem: They break the path registry value.

SetEnvironmentVariable turns the REG_EXPAND_SZ value %SystemRoot%\system32 into a REG_SZ value of C:\Windows\system32.

Any other variables in the path are lost as well. Adding new ones using %myNewPath% won't work any more.

Here's a script Set-PathVariable.ps1 that I use to address this problem:

 [CmdletBinding(SupportsShouldProcess=$true)]
 param(
     [parameter(Mandatory=$true)]
     [string]$NewLocation)

 Begin
 {

 #requires –runasadministrator

     $regPath = "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
     $hklm = [Microsoft.Win32.Registry]::LocalMachine

     Function GetOldPath()
     {
         $regKey = $hklm.OpenSubKey($regPath, $FALSE)
         $envpath = $regKey.GetValue("Path", "", [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
         return $envPath
     }
 }

 Process
 {
     # Win32API error codes
     $ERROR_SUCCESS = 0
     $ERROR_DUP_NAME = 34
     $ERROR_INVALID_DATA = 13

     $NewLocation = $NewLocation.Trim();

     If ($NewLocation -eq "" -or $NewLocation -eq $null)
     {
         Exit $ERROR_INVALID_DATA
     }

     [string]$oldPath = GetOldPath
     Write-Verbose "Old Path: $oldPath"

     # Check whether the new location is already in the path
     $parts = $oldPath.split(";")
     If ($parts -contains $NewLocation)
     {
         Write-Warning "The new location is already in the path"
         Exit $ERROR_DUP_NAME
     }

     # Build the new path, make sure we don't have double semicolons
     $newPath = $oldPath + ";" + $NewLocation
     $newPath = $newPath -replace ";;",""

     if ($pscmdlet.ShouldProcess("%Path%", "Add $NewLocation")){

         # Add to the current session
         $env:path += ";$NewLocation"

         # Save into registry
         $regKey = $hklm.OpenSubKey($regPath, $True)
         $regKey.SetValue("Path", $newPath, [Microsoft.Win32.RegistryValueKind]::ExpandString)
         Write-Output "The operation completed successfully."
     }

     Exit $ERROR_SUCCESS
 }

I explain the problem in more detail in a blog post.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Peter Hahndorf
  • 9,472
  • 4
  • 39
  • 57
9

This sets the path for the current session and prompts the user to add it permanently:

function Set-Path {
    param([string]$x)
    $Env:Path+= ";" +  $x
    Write-Output $Env:Path
    $write = Read-Host 'Set PATH permanently ? (yes|no)'
    if ($write -eq "yes")
    {
        [Environment]::SetEnvironmentVariable("Path",$env:Path, [System.EnvironmentVariableTarget]::User)
        Write-Output 'PATH updated'
    }
}

You can add this function to your default profile, (Microsoft.PowerShell_profile.ps1), usually located at %USERPROFILE%\Documents\WindowsPowerShell.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
JeanT
  • 1,269
  • 10
  • 8
8

Building on @Michael Kropat's answer I added a parameter to prepend the new path to the existing PATHvariable and a check to avoid the addition of a non-existing path:

function Add-EnvPath {
    param(
        [Parameter(Mandatory=$true)]
        [string] $Path,

        [ValidateSet('Machine', 'User', 'Session')]
        [string] $Container = 'Session',

        [Parameter(Mandatory=$False)]
        [Switch] $Prepend
    )

    if (Test-Path -path "$Path") {
        if ($Container -ne 'Session') {
            $containerMapping = @{
                Machine = [EnvironmentVariableTarget]::Machine
                User = [EnvironmentVariableTarget]::User
            }
            $containerType = $containerMapping[$Container]

            $persistedPaths = [Environment]::GetEnvironmentVariable('Path', $containerType) -split ';'
            if ($persistedPaths -notcontains $Path) {
                if ($Prepend) {
                    $persistedPaths = ,$Path + $persistedPaths | where { $_ }
                    [Environment]::SetEnvironmentVariable('Path', $persistedPaths -join ';', $containerType)
                }
                else {
                    $persistedPaths = $persistedPaths + $Path | where { $_ }
                    [Environment]::SetEnvironmentVariable('Path', $persistedPaths -join ';', $containerType)
                }
            }
        }

        $envPaths = $env:Path -split ';'
        if ($envPaths -notcontains $Path) {
            if ($Prepend) {
                $envPaths = ,$Path + $envPaths | where { $_ }
                $env:Path = $envPaths -join ';'
            }
            else {
                $envPaths = $envPaths + $Path | where { $_ }
                $env:Path = $envPaths -join ';'
            }
        }
    }
}
SBF
  • 878
  • 3
  • 9
  • 17
7

My suggestion is this one:

I have tested this to add C:\oracle\x64\bin to environment variable Path permanently and this works fine.

$ENV:PATH

The first way is simply to do:

$ENV:PATH=”$ENV:PATH;c:\path\to\folder”

But this change isn’t permanent. $env:path will default back to what it was before as soon as you close your PowerShell terminal and reopen it again. That’s because you have applied the change at the session level and not at the source level (which is the registry level). To view the global value of $env:path, do:

Get-ItemProperty -Path ‘Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment’ -Name PATH

Or more specifically:

(Get-ItemProperty -Path ‘Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment’ -Name PATH).path

Now to change this, first we capture the original path that needs to be modified:

$oldpath = (Get-ItemProperty -Path ‘Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment’ -Name PATH).path

Now we define what the new path should look like. In this case we are appending a new folder:

$newpath = “$oldpath;c:\path\to\folder”

Note: Be sure that the $newpath looks how you want it to look. If not, then you could damage your OS.

Now apply the new value:

Set-ItemProperty -Path ‘Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment’ -Name PATH -Value $newPath

Now do one final check that it looks like how you expect it to:

(Get-ItemProperty -Path ‘Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment’ -Name PATH).Path

You can now restart your PowerShell terminal (or even reboot the machine) and see that it doesn’t rollback to its old value again.

Note the ordering of the paths may change so that it’s in alphabetical order, so make sure you check the whole line. To make it easier, you can split the output into rows by using the semi-colon as a delimiter:

($env:path).split(“;”)
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
ali Darabi
  • 177
  • 2
  • 1
5

Most answers aren't addressing UAC. This covers UAC issues.

First install PowerShell Community Extensions: choco install pscx via http://chocolatey.org/ (you may have to restart your shell environment).

Then enable pscx

Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser #allows scripts to run from the interwebs, such as pcsx

Then use Invoke-Elevated

Invoke-Elevated {Add-PathVariable $args[0] -Target Machine} -ArgumentList $MY_NEW_DIR
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Jonathan
  • 5,179
  • 4
  • 41
  • 59
5

As Jonathan Leaders mentioned here, it is important to run the command/script elevated to be able to change environment variables for 'machine', but running some commands elevated doesn't have to be done with the Community Extensions, so I'd like to modify and extend JeanT's answer in a way, that changing machine variables also can be performed even if the script itself isn't run elevated:

function Set-Path ([string]$newPath, [bool]$permanent=$false, [bool]$forMachine=$false )
{
    $Env:Path += ";$newPath"

    $scope = if ($forMachine) { 'Machine' } else { 'User' }

    if ($permanent)
    {
        $command = "[Environment]::SetEnvironmentVariable('PATH', $env:Path, $scope)"
        Start-Process -FilePath powershell.exe -ArgumentList "-noprofile -command $Command" -Verb runas
    }

}
lantrix
  • 387
  • 5
  • 16
Mehrdad Mirreza
  • 794
  • 8
  • 20
5

Within PowerShell, one can navigate to the environment variable directory by typing:

Set-Location Env:

This will bring you to the Env:> directory. From within this directory:

To see all environment variables, type:

Env:\> Get-ChildItem

To see a specific environment variable, type:

Env:\> $Env:<variable name>, e.g. $Env:Path

To set an environment variable, type:

Env:\> $Env:<variable name> = "<new-value>", e.g. $Env:Path="C:\Users\"

To remove an environment variable, type:

Env:\> remove-item Env:<variable name>, e.g. remove-item Env:SECRET_KEY

More information is in About Environment Variables.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Paul Maurer
  • 121
  • 1
  • 4
5

To be clear, the 1990's Windows way of click on Start, right click on This PC, and choose Properties, and then select Advanced system settings, and then in the dialog box that pops up, select Environment Variables, and in the list double clicking on PATH and then using the New, Edit, Move Up and Move Down all still work for changing the PATH. Power shell, and the rest of Windows get whatever you set here.

Yes you can use these new methods, but the old one still works. And at the base level all of the permanent change methods are controlled ways of editing your registry files.

Mac
  • 301
  • 2
  • 5
4

Only the answers that push the value into the registry affect a permanent change (so the majority of answers on this thread, including the accepted answer, do not permanently affect the Path).

The following function works for both Path / PSModulePath and for User / System types. It will also add the new path to the current session by default.

function AddTo-Path {
    param ( 
        [string]$PathToAdd,
        [Parameter(Mandatory=$true)][ValidateSet('System','User')][string]$UserType,
        [Parameter(Mandatory=$true)][ValidateSet('Path','PSModulePath')][string]$PathType
    )

    # AddTo-Path "C:\XXX" "PSModulePath" 'System' 
    if ($UserType -eq "System" ) { $RegPropertyLocation = 'HKLM:\System\CurrentControlSet\Control\Session Manager\Environment' }
    if ($UserType -eq "User"   ) { $RegPropertyLocation = 'HKCU:\Environment' } # also note: Registry::HKEY_LOCAL_MACHINE\ format
    $PathOld = (Get-ItemProperty -Path $RegPropertyLocation -Name $PathType).$PathType
    "`n$UserType $PathType Before:`n$PathOld`n"
    $PathArray = $PathOld -Split ";" -replace "\\+$", ""
    if ($PathArray -notcontains $PathToAdd) {
        "$UserType $PathType Now:"   # ; sleep -Milliseconds 100   # Might need pause to prevent text being after Path output(!)
        $PathNew = "$PathOld;$PathToAdd"
        Set-ItemProperty -Path $RegPropertyLocation -Name $PathType -Value $PathNew
        Get-ItemProperty -Path $RegPropertyLocation -Name $PathType | select -ExpandProperty $PathType
        if ($PathType -eq "Path") { $env:Path += ";$PathToAdd" }                  # Add to Path also for this current session
        if ($PathType -eq "PSModulePath") { $env:PSModulePath += ";$PathToAdd" }  # Add to PSModulePath also for this current session
        "`n$PathToAdd has been added to the $UserType $PathType"
    }
    else {
        "'$PathToAdd' is already in the $UserType $PathType. Nothing to do."
    }
}

# Add "C:\XXX" to User Path (but only if not already present)
AddTo-Path "C:\XXX" "User" "Path"

# Just show the current status by putting an empty path
AddTo-Path "" "User" "Path"
YorSubs
  • 1,438
  • 1
  • 12
  • 20
3

Open PowerShell and run:

[Environment]::SetEnvironmentVariable("PATH", "$ENV:PATH;<path to exe>", "USER")
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Jobin James
  • 522
  • 4
  • 10
1

do not make headaches for yourself, want a simple, one line solution to add a permanent environment variable (open powershell in elevated mode):

[Environment]::SetEnvironmentVariable("NewEnvVar", "NewEnvValue", "Machine")

close the session and open it again to make things done


in case that u want to modify/change that:

[Environment]::SetEnvironmentVariable("oldEnvVar", "NewEnvValue", "Machine")


in case that u want to delete/remove that:

[Environment]::SetEnvironmentVariable("oldEnvVar", "", "Machine")
kia nasirzadeh
  • 1,411
  • 10
  • 20
0

I tried to optimise SBF's and Michael's code a bit to make it more compact.

I am relying on PowerShell's type coercion where it automatically converts strings to enum values, so I didn't define the lookup dictionary.

I also pulled out the block that adds the new path to the list based on a condition, so that work is done once and stored in a variable for re-use.

It is then applied permanently or just to the Session depending on the $PathContainer parameter.

We can put the block of code in a function or a ps1 file that we call directly from the command prompt. I went with DevEnvAddPath.ps1.

param(
    [Parameter(Position=0,Mandatory=$true)][String]$PathChange,

    [ValidateSet('Machine', 'User', 'Session')]
    [Parameter(Position=1,Mandatory=$false)][String]$PathContainer='Session',
    [Parameter(Position=2,Mandatory=$false)][Boolean]$PathPrepend=$false
)

[String]$ConstructedEnvPath = switch ($PathContainer) { "Session"{${env:Path};} default{[Environment]::GetEnvironmentVariable('Path', $containerType);} };
$PathPersisted = $ConstructedEnvPath -split ';';

if ($PathPersisted -notcontains $PathChange) {
    $PathPersisted = $(switch ($PathPrepend) { $true{,$PathChange + $PathPersisted;} default{$PathPersisted + $PathChange;} }) | Where-Object { $_ };

    $ConstructedEnvPath = $PathPersisted -join ";";
}

if ($PathContainer -ne 'Session') 
{
    # Save permanently to Machine, User
    [Environment]::SetEnvironmentVariable("Path", $ConstructedEnvPath, $PathContainer);
}

# Update the current session
${env:Path} = $ConstructedEnvPath;

I do something similar for a DevEnvRemovePath.ps1.

param(
    [Parameter(Position=0,Mandatory=$true)][String]$PathChange,

    [ValidateSet('Machine', 'User', 'Session')]
    [Parameter(Position=1,Mandatory=$false)][String]$PathContainer='Session'
)

[String]$ConstructedEnvPath = switch ($PathContainer) { "Session"{${env:Path};} default{[Environment]::GetEnvironmentVariable('Path', $containerType);} };
$PathPersisted = $ConstructedEnvPath -split ';';

if ($PathPersisted -contains $PathChange) {
    $PathPersisted = $PathPersisted | Where-Object { $_ -ne $PathChange };

    $ConstructedEnvPath = $PathPersisted -join ";";
}

if ($PathContainer -ne 'Session') 
{
    # Save permanently to Machine, User
    [Environment]::SetEnvironmentVariable("Path", $ConstructedEnvPath, $PathContainer);
}

# Update the current session
${env:Path} = $ConstructedEnvPath;

So far, they seem to work.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Eniola
  • 688
  • 1
  • 7
  • 20