Search code examples
powershell

How to resolve variables in a Powershell script block


Given I have:

$a = "world"
$b = { write-host "hello $a" }

How do I get the resolved text of the script block, which should be the entre string including write-host:

write-host "hello world"

UPDATE: additional clarifications

If you just print $b you get the variable and not the resolved value

write-host "hello $a"

If you execute the script block with & $b you get the printed value, not the contents of the script block:

hello world

This question is seeking a string containing the contents of the script block with the evaluated variables, which is:

write-host "hello world"

Solution

  • As in the original question, if your entire scriptblock contents is not a string (but you want it to be) and you need variable substitution within the scriptblock, you can use the following:

    $ExecutionContext.InvokeCommand.ExpandString($b)
    

    Calling .InvokeCommand.ExpandString($b) on the current execution context will use the variables in the current scope for substitution.

    The following is one way to create a scripblock and retrieve its contents:

    $a = "world"
    $b = [ScriptBlock]::create("write-host hello $a")
    $b
    
    write-host hello world
    

    You can use your scriptblock notation {} as well to accomplish the same thing, but you need to use the & call operator:

    $a = "world"
    $b = {"write-host hello $a"}
    & $b
    
    write-host hello world
    

    A feature to using the method above is that if you change the value of $a at any time and then call the scriptblock again, the output will be updated like so:

    $a = "world"
    $b = {"write-host hello $a"}
    & $b
    write-host hello world
    $a = "hi"
    & $b
    write-host hello hi
    

    The GetNewClosure() method can be used to create a clone of the scriptblock above to take a theoretical snapshot of the scriptblock's current evaluation. It will be immune to the changing of the $a value later the code:

    $b = {"write-host hello $a"}.GetNewClosure()
    & $b
    write-host hello world
    $a = "new world"
    & $b
    write-host hello world
    

    The {} notation denotes a scriptblock object as you probably already know. That can be passed into Invoke-Command, which opens up other options. You can also create parameters inside of the scriptblock that can be passed in later. See about_Script_Blocks for more information.