最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

powershell - How to recreate a ScriptBlock serialized to a String when passed into Start-Job via -ArgumentList? - Stack Overflow

programmeradmin3浏览0评论

I want to pass a ScriptBlock as an argument into a Start-Job ScriptBlock. I understand that [ScriptBlock] is serialized to a string for safety when passed into a job. So, I need to deserialize it. I've tried using [ScriptBlock]::Create() but it doesn't seem to process the $($args[0].cheese) correctly.

This is a simplied version of what I'm doing.

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


# stuff before this is just to demonstrate that it works outside of Start-Job 


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

# clean up

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

The output is:

My favourite cheese is: cheddar
My favourite cheese is: 

How can I deserialize the string such that the $($args[0].cheese) works?

The example above is pared down to the bone, the real script is 00s of lines and many functions. I don't want to rewrite the function if I can avoid it because it's used in many other locations.

I'm running the built-in PowerShell 5.1.

I want to pass a ScriptBlock as an argument into a Start-Job ScriptBlock. I understand that [ScriptBlock] is serialized to a string for safety when passed into a job. So, I need to deserialize it. I've tried using [ScriptBlock]::Create() but it doesn't seem to process the $($args[0].cheese) correctly.

This is a simplied version of what I'm doing.

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


# stuff before this is just to demonstrate that it works outside of Start-Job 


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

# clean up

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

The output is:

My favourite cheese is: cheddar
My favourite cheese is: 

How can I deserialize the string such that the $($args[0].cheese) works?

The example above is pared down to the bone, the real script is 00s of lines and many functions. I don't want to rewrite the function if I can avoid it because it's used in many other locations.

I'm running the built-in PowerShell 5.1.

Share Improve this question asked Feb 15 at 16:17 user2871239user2871239 1,5722 gold badges12 silver badges31 bronze badges 2
  • This is not Deserializtion. A Scriptblock is a bunch of powershell commands. I think you want to pass a powershell object into the function. Not a scriptblock. – jdweng Commented Feb 15 at 16:46
  • 1 @jdweng, using a background job of necessity involves (serialization and) deserialization. The explicit stated goal, which is perfectly appropriate for the use case, is to pass a script block to the background job, which requires manual deserialization of the string that PowerShell's own deserialization turns the script block into (for dubious reasons). Thus, it's not clear what your comment is trying to tell us; please consider deleting it. – mklement0 Commented Feb 15 at 17:56
Add a comment  | 

1 Answer 1

Reset to default 1
  • Recreating your script block from a string using [scriptblock]::Create() works as intended.

    • As an aside: the alleged security considerations that resulted in choosing to deserialize script blocks as strings have never been spelled out. Personally, I don't see any risk associated with deserializing script blocks as such, given that construction of a script block does not result in its execution.

    • See GitHub issue #11698 for a discussion.

  • Your only problem is the accidental typing of your -Object parameter as [String]$Obj, whereas it should be [object] $Obj (or simply $Obj).

    • A [string] object obviously doesn't have a .cheese property, and, by default, PowerShell quietly evaluates attempts to access non-existent properties as $null, which in the context of string expansion (interpolation) evaluates to the empty string (which is what you saw).

    • You could have caught this problem via Set-StrictMode -Version 2 or higher, which reports attempts to access non-existent properties as errors, though note the additional constraints imposed by these modes.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论