You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何还原通过Start-Job传递的序列化ScriptBlock并正常解析变量

PowerShell 5.1中Start-Job传递ScriptBlock后参数解析问题解决方法

问题场景

现有函数Format-Text接收对象和格式化用的ScriptBlock,外部调用正常,但将其放入Start-Job中执行时,ScriptBlock里的$($args[0].cheese)无法正确解析,输出为空。简化代码及运行结果如下:

原示例代码

function Format-Text {
    [CmdletBinding()]
    param([Parameter(Mandatory, ValueFromPipeline)][Object]$InputObject,
          [Parameter(Mandatory)][ScriptBlock]$Formatter)
    Write-Host ($Formatter.Invoke($InputObject))
}

$formatter = {"My favourite cheese is: $($args[0].cheese)"}
$testObject = [PSCustomObject]@{cheese = 'cheddar'}

Format-Text -InputObject $testObject -Formatter $formatter


# 作业内执行部分
$job = Start-Job -ArgumentList $testObject,$formatter.ToString() -ScriptBlock {
    param ([String]$Obj,
           [String]$Fmtr)

    function Format-Text {
        [CmdletBinding()]
        param([Parameter(Mandatory, ValueFromPipeline)][Object]$InputObject,
              [Parameter(Mandatory)][ScriptBlock]$Formatter)
        Write-Host ($Formatter.Invoke($InputObject))
    }

    $sb = [ScriptBlock]::Create($Fmtr)

    Format-Text -InputObject $Obj -Formatter $sb
}

# 作业清理
do { Start-Sleep -Milliseconds 500 } until ($job.State -ne 'Running')
Receive-Job $job; Remove-Job $job

原运行输出

My favourite cheese is: cheddar
My favourite cheese is: 

问题原因

  1. 对象类型错误:作业内部参数将$Obj定义为[String],导致传入的PSCustomObject被强制转换为字符串,丢失了原对象的属性结构,因此$args[0].cheese无法找到对应属性。
  2. ScriptBlock传递时的序列化问题可通过[ScriptBlock]::Create()还原,核心问题在于对象传递后的类型丢失。

解决方案

无需重写现有Format-Text函数,只需修改作业内的两个关键点:

  • 将作业参数中的$Obj类型从[String]改为[Object],保留原对象的结构;
  • 传递ScriptBlock时无需手动调用.ToString(),直接传入$formatter即可(跨作业边界时PowerShell会自动将ScriptBlock序列化为字符串)。

修改后的完整代码

function Format-Text {
    [CmdletBinding()]
    param([Parameter(Mandatory, ValueFromPipeline)][Object]$InputObject,
          [Parameter(Mandatory)][ScriptBlock]$Formatter)
    Write-Host ($Formatter.Invoke($InputObject))
}

$formatter = {"My favourite cheese is: $($args[0].cheese)"}
$testObject = [PSCustomObject]@{cheese = 'cheddar'}

Format-Text -InputObject $testObject -Formatter $formatter


# 修改后的作业执行部分
$job = Start-Job -ArgumentList $testObject, $formatter -ScriptBlock {
    param ([Object]$Obj,  # 改为Object类型,保留原对象结构
           [String]$Fmtr) # 直接接收序列化后的ScriptBlock字符串

    function Format-Text {
        [CmdletBinding()]
        param([Parameter(Mandatory, ValueFromPipeline)][Object]$InputObject,
              [Parameter(Mandatory)][ScriptBlock]$Formatter)
        Write-Host ($Formatter.Invoke($InputObject))
    }

    $sb = [ScriptBlock]::Create($Fmtr)

    Format-Text -InputObject $Obj -Formatter $sb
}

# 作业清理
do { Start-Sleep -Milliseconds 500 } until ($job.State -ne 'Running')
Receive-Job $job; Remove-Job $job

验证输出

修改后运行代码,会得到一致的正确输出:

My favourite cheese is: cheddar
My favourite cheese is: cheddar

内容的提问来源于stack exchange,提问作者user2871239

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 09:57:06