Managing Environment Variables with PowerShell

Working with environment variables in Windows is as easy as:

Win + Pause > "Advanced system settings" > "Environment Variables..."

After which you get a tiny, unresizable, form where you can view and manage them. Something better eventually arrived with Windows 10 but still, PowerShell :)

Use Autohotkey to open the window with Left Alt + Pause:

LAlt & Pause::Run % "rundll32 sysdm.cpl,EditEnvironmentVariables"

Category:

dev-setup

Tags:

powershellwindows

Share this article on:

If you’d like a GUI instead you could try: rix0rrr/WindowsPathEditor:

Getting environment variables

# Listing all environment variables
Get-ChildItem Env:
env

# Or just those containing path
Get-ChildItem Env:*PATH* | Format-List

# Display a specific one (case insensitive)
$env:PATH
[environment]::GetEnvironmentVariable("PATH", "Process")

# They are the union of
# Machine: HKEY_LOCAL_MACHINESystemCurrentControlSetControlSession ManagerEnvironment
[environment]::GetEnvironmentVariable("PATH", "Machine")
# User: HKEY_CURRENT_USEREnvironment
[environment]::GetEnvironmentVariable("PATH", "User")

In cmd.exe, all envs can be listed with set.

Managing environment variables

# Managing current session environment
$env:test = "value"
Remove-Item Env:test

# Persistent registry management (registry)
[Environment]::SetEnvironmentVariable("yaye", "value", "User")
[Environment]::SetEnvironmentVariable("yaye", "$null", "User")

# Requires RunAs Admin
[Environment]::SetEnvironmentVariable("yaye", "value", "Machine")

When using the PowerShell $env:key = "val", the environment changes are available only for the current session. When using the .NET [Environment]::SetEnvironmentVariable, the changes are persisted to the registry but they do not become available in the current session.

A helper function to the rescue!

Set-Alias se Set-Environment

function Set-Environment([String]$key, [String]$value) {
	[System.Environment]::SetEnvironmentVariable("$key", "$value", "User")
	Set-Item -Path Env:$key -Value $value
}

Already opened processes, tabs, etc won’t see these environment changes. The environment can be refreshed from the registry however.

Set-Alias re Refresh-Environment

function Refresh-Environment {
	$locations = 'HKLM:SYSTEMCurrentControlSetControlSession ManagerEnvironment', 'HKCU:Environment'
	$locations | ForEach-Object {
		$k = Get-Item $_
		$k.GetValueNames() | ForEach-Object {
			$name = $_
			$value = $k.GetValue($_)
			Set-Item -Path Env:$name -Value $value
		}
	}

	# Put Machine and User $env:PATH together
	$machinePath = [Environment]::GetEnvironmentVariable("PATH", "Machine")
	$userPath = [Environment]::GetEnvironmentVariable("PATH", "User")
	$Env:PATH = "$machinePath;$userPath"
}

If you have chocolatey installed, RefreshEnv will also work.


$env:PATH helpers

Use fp to list all directories in $env:path. It accepts a search needle as parameter. ex: fp node.

By using the PowerShell builtin variable $args, it is not required to put quotes around directories containing spaces.

Set-Alias fp Find-EnvironmentPath

function Find-EnvironmentPath {
	$needle = ($args -join " ").TrimEnd("")
	$needle = [Regex]::Escape($needle)

	$env:PATH.split(";") |
		Where-Object { $_.TrimEnd("") -match $needle } |
		Sort-Object |
		Get-Unique -AsString
}

Append a directory to the User path with ap c:bins.

Set-Alias ap Append-EnvironmentPath

function Append-EnvironmentPath {
	$toAppend = ($args -join " ").TrimEnd("")
	if (-not (Test-Path $toAppend)) {
		Write-Host "Path doesn't exist..."
		Write-Host $toAppend
		return
	}
	$env:PATH = $env:PATH + ";$toAppend"
	$userPath = [Environment]::GetEnvironmentVariable("PATH", "User")
	[Environment]::SetEnvironmentVariable("PATH", "$userPath;$toAppend", "User")
}

Remove a directory from the User path with delp c:Program FilessomeBin.

Set-Alias delp Remove-EnvironmentPath

function Remove-EnvironmentPath {
	$toRemove = ($args -join " ").TrimEnd("")

	$userPath = [Environment]::GetEnvironmentVariable("PATH", "User")
	$changedUserPath = $userPath.Split(";") |
		ForEach-Object { $_.TrimEnd("") } |
		Where-Object { $_ -ne $toRemove }

	$changedUserPath = $changedUserPath -Join ";"

	if ($userPath -ne $changedUserPath) {
		[Environment]::SetEnvironmentVariable("PATH", "$changedUserPath", "User")
		$env:PATH = [Environment]::GetEnvironmentVariable("PATH", "Machine")
		$env:PATH += ";$changedUserPath"
		return
	}

	echo "Not removed"

	$machinePath = [Environment]::GetEnvironmentVariable("PATH", "Machine")
	$isInMachine = $machinePath.split(";") | Where-Object { $_.Trim("") -eq $toRemove }
	if ($isInMachine) {
		echo "Is present in Machine scope"
	}
}

Useful environment variables

Win + Pause: Control PanelSystem and SecuritySystem
PowerShell: $env:USERNAME
Cmd: %USERNAME%

These are probably worth remembering.

"$env:USERNAME@$env:USERDOMAIN"

$env:APPDATA
# C:UsersWouterAppDataRoaming

$env:HOME
$env:USERPROFILE
# C:UsersWouter

$env:LOCALAPPDATA
# C:UsersWouterAppDataLocal

$env:TEMP
# C:UsersWouterAppDataLocalTemp

Extras

Stuff that came into being during the making of this post

Updates

  • 25 August 2018