0

I have the following cmdlet to invoke an arbitrary scriptblock (that usually calls an exe) and handles the return code. The goal here is to print a command line, run it, and then throw an error if it fails....


function Invoke-ScriptBlock {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [scriptblock]$Block
    )

    $ErrorActionPreference = 'Continue'

    $stringBlock = "@`"`n" + $Block.ToString().Trim() + "`n`"@"
    $stringValue = . ([scriptblock]::create($stringBlock))

    Write-Information "Invoking command: $stringValue"

    . $Block
    if ($lastexitcode -ne 0) { Write-Error "Command exited with code $lastexitcode" -EA Stop }
}

$comment = 'acomment'
Invoke-ScriptBlock { git commit -m $comment }
# prints Invoking command: git commit -m acomment

This works great as long as the Invoke-ScriptBlock cmdlet isn't inside a module. If it is, I lose the closure on the variables captured in the "Invoking command" message.

Import-Module .\Util.psm1
$comment = 'acomment'
Invoke-ScriptBlock { git commit -m $comment }
# prints Invoking command: git commit -m

Is there a way I can recreate the scriptblock using the original context from the passed in $Block?

0

1 Answer 1

1

There's no supported way, but you can do it with reflection by copying the session state reference from the original $Block to the recreated script block:

function Invoke-ScriptBlock {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [scriptblock]$Block
    )

    # To prove local and module-scoped variables won't affect the new script block
    $comment = ''

    # Obtain a reference to the relevant internal ScriptBlock property 
    $ssip = [scriptblock].GetProperty('SessionStateInternal',[System.Reflection.BindingFlags]'NonPublic,Instance')

    # Copy the value from $Block
    $ssi = $ssip.GetMethod.Invoke($Block, @())

    # Create new block with the same content 
    $newBlock = [scriptblock]::create("@`"`n" + $Block.ToString().Trim() + "`n`"@")

    # Overwrite session state value with the one with copied from $Block
    $ssip.SetMethod.Invoke($newBlock, @($ssi))

    $stringValue = & $newBlock

    Write-Information "Invoking command: $stringValue"
    & $Block
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.