Skip to content
Menu
The Lonely Administrator
  • PowerShell Tips & Tricks
  • Books & Training
  • Essential PowerShell Learning Resources
  • Privacy Policy
  • About Me
The Lonely Administrator

Friday Fun: Expand Environmental Variables in PowerShell Strings

Posted on June 29, 2012

This week I was working on a project that involved using the %PATH% environmental variable. The challenge was that I have some entries that look like this: %SystemRoot%\system32\WindowsPowerShell\v1.0\. When I try to use that path in PowerShell, it complains because it doesn't expand %SystemRoot%. What I needed was a way to replace it with the actual value, which I can find in the ENV: PSdrive, or reference as $env:systemroot. This seems reasonable enough. Take a string, use a regular expression to find the environmental variable, find the variable in ENV:, do a replacement, write the revised string back to the pipeline. So here I have Resolve-EnvVariable.

Manage and Report Active Directory, Exchange and Microsoft 365 with
ManageEngine ADManager Plus - Download Free Trial

Exclusive offer on ADManager Plus for US and UK regions. Claim now!

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("%") | Where {$_}
foreach ($text in $values) {
#find the corresponding value in ENV:
Write-Verbose "Looking for $text"
[string]$replace=(Get-Item env:$text -erroraction "SilentlyContinue").Value
if ($replace) {
#if found append it to the new string
Write-Verbose "Found $replace"
$newstring+=$replace
}
else {
#otherwise append the original text
$newstring+=$text
}

} #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

The function takes a string as a parameter, or you can pipe into the function. The function looks to see if there is something that might be an environmental variable using a regular expression match.


if ($string -match "%\S+%") {

If there is an extra % character in the string, this won't work so I'm assuming you have some control over what you provide as input. Now I need to get the match value. At first I tried using the Regex object. But when faced with a string like this "I am %username% and working on %computername%" it also tried to turn % and working on% as an environmental variable. I'm sure there's a regex pattern that will work but I found it just as easy to split the string on the % character and trim off the extra space.


$values=$string.split("%") | Where {$_}

Now, I can go through each value and see if there is a corresponding environmental variable.


foreach ($text in $values) {
#find the corresponding value in ENV:
Write-Verbose "Looking for $text"
[string]$replace=(Get-Item env:$text -erroraction "SilentlyContinue").Value

I turned off the error pipeline to suppress errors about unfound entries. If something was found then I do a simple replace, otherwise, I re-use the original text.


if ($replace) {
#if found append it to the new string
Write-Verbose "Found $replace"
$newstring+=$replace
}
else {
#otherwise append the original text
$newstring+=$text
}

In essence I am building a new string adding the replacement values or original text. When finished I can write the new string, which has the variable replacements back to the pipeline.


Write-Verbose "Writing revised string to the pipeline"
#write the string back to the pipeline
Write-Output $NewString

Finally, I can pass strings that contain environmental variables to the function.


PS C:\> "I am %username% and working on %computername%" | resolve-envvariable
I am Jeff and working on SERENITY

This isn't perfect. Look what happens if there is an undefined variable:


PS C:\> "I am %username% and working on %computername% with a %bogus% variable." | resolve-envvariable
I am Jeff and working on SERENITY with a bogus variable.

But as long as you are confident that variables are defined, then you can do things like this:


PS C:\> $env:path.split(";") | Resolve-EnvVariable | foreach { if (-Not (Test-Path $_)) {$_}}
c:\foo

Download Resolve-EnvVariable and let me know what you think. The download version includes comment based help.


Behind the PowerShell Pipeline

Share this:

  • Click to share on X (Opens in new window) X
  • Click to share on Facebook (Opens in new window) Facebook
  • Click to share on Mastodon (Opens in new window) Mastodon
  • Click to share on LinkedIn (Opens in new window) LinkedIn
  • Click to share on Pocket (Opens in new window) Pocket
  • Click to share on Reddit (Opens in new window) Reddit
  • Click to print (Opens in new window) Print
  • Click to email a link to a friend (Opens in new window) Email

Like this:

Like Loading...

Related

20 thoughts on “Friday Fun: Expand Environmental Variables in PowerShell Strings”

  1. JV says:
    June 29, 2012 at 9:12 am

    Jeff- dont look now but ther eis a net lcasds staic method that does exactly that. YOU can also use the shell method but it takes two lines.

    shell.ExpandEnvironmentStrings()

    If I remember correctly “ExpandEnvironmentStrings” is a Win32 API call.

    1. Jeffery Hicks says:
      June 29, 2012 at 9:18 am

      Fine. Rain on my parade!! But remember my Friday Fun articles are as much about concepts and techniques as practical solutions.

  2. JV says:
    June 29, 2012 at 9:16 am

    09:15 PS>$sh = new-object -com WScript.Shell
    09:15 PS>$sh.ExpandEnvironmentStrings(“\\test\%computername%\%username%\myfile.txt”)
    \\test\OMEGA2\jvierra\myfile.txt

    1. Jeffery Hicks says:
      June 29, 2012 at 9:19 am

      Sure, but who wants to use a COM object?! You can tell I’ve been away from VBScript for a long time.

  3. JV says:
    June 29, 2012 at 9:23 am

    Sorry – How about a NEt Framework object exposed in POwerSHell as a type accellerator? Hmm?

    09:17 PS>[environment]::ExpandEnvironmentVariables(‘I am %username% and working on %computername%’)
    I am jvierra and working on OMEGA2

    Sorry – I just stumbled on that myself a couple of days ago. It took me qa few minutes to figure out my memory of it. I tried [env] and [context] etc then searched and found it in a second.

    Nice little bit. It is the foundation for the “env:” provider.

    1. Jeffery Hicks says:
      June 29, 2012 at 9:30 am

      Now that is helpful.

    2. Jeffery Hicks says:
      June 29, 2012 at 9:35 am

      Here’s a short function using this technique:


      Function Resolve-EnvVar {
      [cmdletbinding()]
      Param(
      [Parameter(Position=0,Mandatory=$True,ValueFromPipeline=$True)]
      [string]$string
      )

      Process {
      [environment]::ExpandEnvironmentVariables($string)
      }
      }

      I guess because I’m not a developer I don’t think to look in the framework for a solution. I figure stuff out from what I can see in the shell. But this is nice.


      PS S:\> "%systemroot%\foo" | resolve-envvar | test-path
      False

  4. JV says:
    June 29, 2012 at 9:26 am

    And I say stay away from VBScript – wish I could.
    I didn;t mean to rain on your “parade’ and the echniques aer useful and good. I shows how easy it is in POwerSHell to quickly provide mazing functionality with little effort. Very Good. Keep up the blogging.

    1. Jeffery Hicks says:
      June 29, 2012 at 9:31 am

      So here’s a quick and dirty function using the COM object.


      Function Resolve-EnvVar {
      [cmdletbinding()]
      Param(
      [Parameter(Position=0,Mandatory=$True,ValueFromPipeline=$True)]
      [string]$string
      )

      Begin {
      $shell=new-object -com wscript.shell
      }
      Process {
      $shell.ExpandEnvironmentStrings($string)
      }
      End {}
      }

      This is better at handling undefined variables.


      PS S:\> "I am %username% on %computername% with a %bogus% variable" | resolve-envvar
      I am Jeff on SERENITY with a %bogus% variable

      1. JV says:
        June 29, 2012 at 9:56 am

        WHy is it better? They should both be the same API call.

  5. Jeffery Hicks says:
    June 29, 2012 at 10:19 am

    I don’t care about API calls. What matters is how easy it is to get at it. Some of my comments here are not chronological. The COM and .NET approach both do the same thing. but I’d say the latter is a bit more “economical”.

  6. JV says:
    June 29, 2012 at 10:39 am

    [environment]::ExpandEnvironmentVariables(‘I am %username% and working on %systemroot%’)

    That is economical.

  7. Daniel says:
    June 29, 2012 at 3:32 pm

    Nice little exercise. Thanks for sharing. Good to know that there are API calls for this but ofcourse it takes away the fun. I figured it would be possible to write the same thing using regular expressions;


    Function Resolve-EnvVariable($expr)
    {
    $eval = [System.Text.RegularExpressions.MatchEvaluator]{
    param($m)
    $v = [Environment]::GetEnvironmentVariable($m.ToString().Trim("%"))
    if ($v) { $v } else { $m };
    }
    [regex]::Replace($expr, "%.+?%", $eval)
    }

    1. Jeffery Hicks says:
      June 29, 2012 at 3:52 pm

      That’s a cool way too, although regex always requires beer.

      1. JV says:
        June 29, 2012 at 4:07 pm

        Now Jeff wwent and said the magic word and reminded me that I am out. No more typing today until we remedy the refrigerator situation.
        Good weekend all.

  8. Walid Toumi says:
    July 1, 2012 at 10:06 am

    just for fun:

    Function Resolve-EnvVar {
    [cmdletbinding()]
    Param(
    [Parameter(Position=0,Mandatory=$True,ValueFromPipeline=$True)]
    [string]$string
    )
    iex "$env:ComSpec /c echo $string"
    }

  9. Greg Wojan says:
    July 9, 2012 at 10:24 am

    I’m just a little confused and having trouble with determining a use case. Why not just use "I am $env:username working on $env:computername"? We are using PowerShell after all. ;-)

    1. Jeffery Hicks says:
      July 9, 2012 at 10:35 am

      The issue isn’t expanding PowerShell environmental variables but rather strings that might contain DOS-type environmental variables like %WINDIR%.

  10. Alok says:
    September 26, 2012 at 9:17 pm

    I am facing a very strange issue. I set an environment variable from Powershell and add it in the path as below

    $mypath=[environment]::GetEnvironmentVariable(“PATH”,,”User”)
    [environment]::SetEnvironmentVariable(“TEST”,”MyTestVariable”,”User”)
    [environment]::SetEnvironmentVariable(“PATH”,$mypath+”;%TEST%”,”User”)

    When I want to see the path in windows command shell I see following, which is very strange for me

    c:\>echo %TEST%
    MyTestVariable

    c:\>path
    ;%TEST%;

    in path, the TEST variable is not expanding and i am not able to use this feature. For echo it works fine though. I am very confused.

    1. Jeffery Hicks says:
      September 27, 2012 at 8:24 am

      You told PowerShell to add a literal string %TEST% to your path and it did. PowerShell can’t expand it like that. What you did was the same as this, which is much easier.

      [environment]::SetEnvironmentVariable(“Path”,”$env:path;%TEST%”,”user”)

      The CMD shell can’t expand that in the path. Why not just do this:

      [environment]::SetEnvironmentVariable(“Path”,”$env:path;c:\work”,”user”)

      If you need more help, I suggest using the forums at PowerShell.org.

Comments are closed.

reports

Powered by Buttondown.

Join me on Mastodon

The PowerShell Practice Primer
Learn PowerShell in a Month of Lunches Fourth edition


Get More PowerShell Books

Other Online Content

github



PluralSightAuthor

Active Directory ADSI Automation Backup Books CIM CLI conferences console Friday Fun FridayFun Function functions Get-WMIObject GitHub hashtable HTML Hyper-V Iron Scripter ISE Measure-Object module modules MrRoboto new-object objects Out-Gridview Pipeline PowerShell PowerShell ISE Profile prompt Registry Regular Expressions remoting SAPIEN ScriptBlock Scripting Techmentor Training VBScript WMI WPF Write-Host xml

©2025 The Lonely Administrator | Powered by SuperbThemes!
%d