【问题标题】:How do I pass a scriptblock as one of the parameters in start-job如何将脚本块作为启动作业中的参数之一传递
【发布时间】:2012-08-07 10:54:04
【问题描述】:

我正在尝试创建一个执行脚本块的后台作业。我需要将此脚本块作为参数传递,但我似乎无法使语法正常工作。脚本块正在一路转换为字符串。

当我将脚本块传递给本地函数时它工作正常,但不是通过 start-job

以下语法有效:

function LocalFunction
{
    param (
        [parameter(Mandatory=$true)]
        [ScriptBlock]$ScriptBlock
    )

    &$ScriptBlock | % { echo "got $_" }
}

LocalFunction -ScriptBlock { echo "hello" }

这会按预期输出“got hello”。

但以下失败:

$job = start-job -argumentlist { echo "hello" } -scriptblock {
    param (
        [parameter(Mandatory=$true)]
        [ScriptBlock]$ScriptBlock
    )
    &$ScriptBlock | % { echo "got $_" }
}
start-sleep -s 1
receive-job $job

它返回的错误是

Receive-Job : Cannot process argument transformation on parameter 'ScriptBlock'. Cannot convert the " echo "hello" " value of type "System.String" to type "System.Management.Automation.ScriptBlock".

所以如果我没看错,似乎 -argumentlist 以某种方式将其参数强制转换为字符串。

任何 powershell 大师都知道如何使这种语法起作用吗?

提前致谢,

【问题讨论】:

  • 我在使用psake 时遇到了同样的问题。我创建了一个前提条件$prec = { ... },并尝试将同样的前提条件传递给各种任务。我得到与您相同的错误。如果您找到解决方案,请告诉我..

标签: powershell


【解决方案1】:

这是解决这个问题的一种方法,将脚本块代码作为字符串传递,然后从作业中的字符串创建一个脚本块并执行它

Start-Job -ArgumentList "write-host hello"  -scriptblock {

    param (
        [parameter(Mandatory=$true)][string]$ScriptBlock
    )

    & ([scriptblock]::Create($ScriptBlock))

} | Wait-Job | Receive-Job

【讨论】:

  • 这并没有回答如何传递脚本块的问题,我也需要这样做。
  • 不,你是对的,它没有直接回答问题,但它提供了一种解决方法。据我所知,参数是序列化的(就像在远程处理中一样),并且脚本块仅作为字符串传递。解决方法是使用 ScriptBlock.Create 方法。
  • 谢谢。生病使用字符串来传递代码。我从您所说的中收集到:即使不使用网络,powershell 也会始终序列化参数,而只是将它们从一种方法传递到另一种方法。
  • 澄清一下,PowerShell 序列化 Job 和 Remoting cmdlet 的参数。
  • 很好的解决方法;脚本块反序列化为字符串是(令人惊讶的)设计:“从历史上看,这是设计使然。以保真度序列化脚本块导致有太多地方自动执行代码,因此为了促进安全受限的运行空间,脚本块总是反序列化为字符串。” - github.com/PowerShell/PowerShell/issues/…
【解决方案2】:

看起来今天这样有效。

function LocalFunction
{
    param
    (
        [scriptblock] $block
    )

    $block.Invoke() | % { "got $_" }
}

LocalFunction { "Hello"} 

【讨论】:

  • 我认为这一直有效,因为没有涉及 作业(或远程处理)(如上所述,问题是 序列化 创建作业/远程处理时隐含涉及的内容)。顺便说一句,调用脚本块的更惯用的方法是使用 & 运算符(或者,如果您不想为变量创建子范围,则使用 . 运算符)。
【解决方案3】:

根据我的实验,PowerShell 将 -ArgumentList(它是一个 Object[])解析为字符串,即使您传入脚本块也是如此。以下代码:

$job = start-job -scriptblock { $args[0].GetType().FullName } -argumentlist { echo "hello" }
start-sleep -s 1
receive-job $job

产生以下输出:

系统字符串

据我所知,这里唯一的解决方案是 Shay's,尽管您不需要将 -ArgumentList 作为字符串传递,因为在这种情况下 PowerShell 会将您的脚本块解析为字符串。

【讨论】:

  • 很好的演示了这个问题,但是,严格来说,问题不在于-ArgumentList如何为解析脚本块,而在于它们被转换为字符串关于反序列化(正如 Shay 指出的那样,在创建工作/进行远程处理时隐含地涉及)。
【解决方案4】:

您必须将其作为字符串读入,然后将其转换为脚本块。

在 powershell v1 中你可以这样做:

$ScriptBlock = $executioncontext.invokecommand.NewScriptBlock($string)

在 powershell v2 中你可以这样做:

$ScriptBlock = [scriptblock]::Create($string)

所以你的代码应该是这样的:

function LocalFunction
{
    param (
        [parameter(Mandatory=$true)]
        $ScriptBlock
    )

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

    $sb | % { echo "got $_" }
}

LocalFunction -ScriptBlock "echo 'hello'"

'[scriptblock]::Create($ScriptBlock)' 将在字符串周围放置花括号,以便您创建脚本块。

在这里找到信息http://get-powershell.com/post/2008/12/15/ConvertTo-ScriptBlock.aspx

【讨论】:

  • 您缺少脚本块的调用:使用& $sb | ... 而不是$sb | ...
【解决方案5】:

因此,如果您希望插入内联脚本块,那么 Shay 的解决方案(如前所述)可能是最好的。另一方面,如果您只是想将脚本块作为参数传递,请考虑使用scriptblock 类型的变量,然后将其作为-ScriptBlock 参数的值传递。

function LocalFunction
{
    param (
        [parameter(Mandatory=$true)]
        [ScriptBlock]$ScriptBlock
    )

    &$ScriptBlock | % { echo "got $_" }
}

[scriptblock]$sb = { echo "hello" }

LocalFunction -ScriptBlock $sb

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-08
    • 2011-11-01
    • 1970-01-01
    相关资源
    最近更新 更多