Skip to content

Instantly share code, notes, and snippets.

@alshain
Last active May 26, 2019 09:30
Show Gist options
  • Save alshain/0376c093b3436127b118604518a88058 to your computer and use it in GitHub Desktop.
Save alshain/0376c093b3436127b118604518a88058 to your computer and use it in GitHub Desktop.
Resolve-Variable.ps1
# Todo create cmdlet
# TODOs
# Does not properly recognize variable expansions, because GetEnvironmentVariables already does that... probably with the current process' environment variables
#
function GetCurrentUserPathVariableRaw() {
if ($env:USERDOMAIN.Length -eq 0) {
throw "Does not yet support no domains"
}
$userFilter = "$env:USERDOMAIN\\$env:USERNAME"
Write-Output (Get-WmiObject -Query "select * from Win32_Environment WHERE Name = 'PATH' and UserName = '$userFilter'").VariableValue
}
function GetSystemPathVariableRaw() {
Write-Output (Get-WmiObject -Query "select * from Win32_Environment WHERE Name = 'PATH' and SystemVariable = true").VariableValue
}
function NormalizedPath($path) {
return (Join-Path $path "")
}
function NormalizedPathToLower() {
param (
# Path
[Parameter()]
[string]
$Path
)
return (Join-Path $Path "").ToLowerInvariant()
}
function ContainsEnvironmentVars() {
param (
# Path
[Parameter()]
[string]
$pathEntry
)
return $pathEntry -match "%\S+\%"
}
# Adapted from:
# https://powershell.org/2012/06/friday-fun-expand-environmental-variables-in-powershell-strings/
#
# Fixes: "%SYSTEMROOT%\" (Get-Item would expand `\` to *all* ENV variables)
# Fixes: "%SYSTEMROOT%PATH" Would expand both SYSTEMROOT *and* PATH, instead of only %SYSTEMROOT%
# Trying to be consistent with what CMD does in its expansion
# echo "%SYSTEMROOT%PATH%"" will evaluate to `<SYSTEMROOT>PATH%`
# echo "%SYSTEMROOT%%PATH%"" will evaluate to `<SYSTEMROOT><PATH>`
# echo "%SYSTEMROOT%%%PATH%"" will evaluate to `<SYSTEMROOT><PATH>`
# echo "%DOESNOTEXIST%PATH%"" will evaluate to `DOESNOTEXIST<PATH>`
Function Resolve-EnvVariable {
[cmdletbinding()]
Param(
[Parameter(Position=0,ValueFromPipeline=$True,Mandatory=$True,
HelpMessage="Enter a string that contains an environmental variable like %WINDIR%")]
[ValidateNotNullOrEmpty()]
[string]$String
)
Begin {
Write-Verbose "Starting $($myinvocation.mycommand)"
} #Begin
Process {
#if string contains a % then process it
if ($string -match "%\S+%") {
Write-Verbose "Resolving environmental variables in $String"
#split string into an array of values
$values=$string.Split("%")
# We only enounter a variable after the first `%`
$isVariable = $false
$totalFragments = $values.Count
$fragment = 0;
foreach ($text in $values) {
$fragment++;
if ($totalFragments -eq $fragment) {
# If we're looking at the last fragment, it can't be a variable because it would need to be followed by another `%`
# Prevents trailing `%PATH` in $String from being parsed as a variable
if ($isVariable) {
$isVariable = $false;
# We need to add the percentage sign manually here for the "unresolved" variable
$newstring += "%$text"
# We're at the end anyway, but we need to prevent adding `$text` twice.
# We can't use the regular non-variable case instead, because then we'd be missing the `%`
break
}
}
#find the corresponding value in ENV:
if ($isVariable) {
Write-Verbose "Looking for $text"
# Prevent accessing "\" element by accident, assuming there are always more than one ENV variable
$variable = (Get-Item env:$text -erroraction "SilentlyContinue")
# In case we end up
$success = $false
$resultCount = ($variable | Measure-Object).Count
if ($text -eq "\" -or $text -eq ".") {
# Special case this information
Write-Verbose "Tried to access $text, this would access ALL env variables, skipping..."
}
elseif ($resultCount -gt 1) {
Write-Verbose "Found too many results, potentially accessed `\`: $variable"
}
elseif ($resultCount -eq 0) {
Write-Verbose "Did not find $text"
}
# Separate IF such that we can consolidate error handling
if ($resultCount -eq 1) {
$replace = $variable.Value
#if found append it to the new string
Write-Verbose "Found $replace"
$newstring+=$replace
# Only here we actually found our variable, in the other cases, the next % sign can again start a variable
$isVariable = $false
} else {
#otherwise append the original text
# Problem: If there is an unbalanced amount of `%` in the string, this will balance it incorrectly
# We can't put both `%` here, because we don't know whether this string actually
$newstring += "%$text"
# Don't reset isVariable to false, because `cmd > echo %DOESNOTEXIST%PATH%` would try to match on `%PATH` next
}
} else {
# Don't log empty parts
if ($text) {
Write-Verbose "Appending non-variable part for $text"
$newstring += $text
}
$isVariable = $true
}
} #foreach value
Write-Verbose "Writing revised string to the pipeline"
#write the string back to the pipeline
Write-Output $NewString
} #if
else {
#skip the string and write it back to the pipeline
Write-Output $String
}
} #Process
End {
Write-Verbose "Ending $($myinvocation.mycommand)"
} #End
} #end Resolve-EnvVariable
function Test-Resolve-EnvVariable() {
param(
# Test-String
[Parameter(Mandatory=$true)]
[string]
$String,
# Expected Value
[Parameter(Mandatory=$true)]
[string]
$Expected
)
if ((Resolve-EnvVariable $String) -ne $Expected) {
$result = Resolve-EnvVariable $String -Verbose
Write-Host "Resolve-EnvVariable for: $String"
Write-Host "> should expand to: $Expected"
Write-Host "> instead, it resolved to: $result"
Write-Host $result
Write-Host
Write-Host "See above for VERBOSE trace"
}
else {
Write-Host "Resolve-EnvVariable $($String): OK"
}
}
Test-Resolve-EnvVariable "%SYSTEMROOT%\" "EXPECT-MISMATCH" # Sanity test
Test-Resolve-EnvVariable "%SYSTEMROOT%\" "$env:SYSTEMROOT\"
Test-Resolve-EnvVariable "%SYSTEMROOT%PATH%" "$($env:SYSTEMROOT)PATH%"
Test-Resolve-EnvVariable "%SYSTEMROOT%%PATH%" "$($env:SYSTEMROOT)$($env:PATH)"
Test-Resolve-EnvVariable "%SYSTEMROOT%%%PATH" "$env:SYSTEMROOT%%PATH"
Test-Resolve-EnvVariable "%SYSTEMROOT%%%PATH%" "$env:SYSTEMROOT%$env:PATH"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment