问题描述
我具有以下cmdlet来调用任意脚本块(通常调用exe)并处理返回代码。这里的目标是打印一个命令行,运行它,然后在失败时抛出一个错误....
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
只要Invoke-ScriptBlock cmdlet不在模块内部,此方法就很好用。如果是这样,我将丢失“调用命令”消息中捕获的变量的闭包。
Import-Module .\Util.psm1
$comment = 'acomment'
Invoke-ScriptBlock { git commit -m $comment }
# prints Invoking command: git commit -m
有没有一种方法可以使用传入的$ Block中的原始上下文重新创建脚本块?
解决方法
没有受支持的方法,但是您可以通过将会话状态引用从原始$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
}