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