首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >什么时候ScriptBlock不是ScriptBlock?

什么时候ScriptBlock不是ScriptBlock?
EN

Stack Overflow用户
提问于 2014-09-08 16:15:45
回答 4查看 995关注 0票数 3

我不想用这个问题听起来太可爱,但这确实是眼前的问题。考虑在安装在PowerShell下的Test.psm1模块Test.psm1中定义的以下两个函数:

代码语言:javascript
复制
function Start-TestAsync
{
    [CmdletBinding()]
    param([ScriptBlock]$Block, [string]$Name = '')   
    Start-Job { Start-Test -Name $using:Name -Block $using:Block }
}

function Start-Test
{
    [CmdletBinding()]
    param([ScriptBlock]$Block, [string]$Name = '')
    # do some work here, including this:
    Invoke-Command -ScriptBlock $Block
}

导入模块后,我可以运行同步函数.

代码语言:javascript
复制
PS> Start-Test -Name "My Test" -Block { ps | select -first 9 }

...and显示来自Get进程的适当输出。

但是,当我试图运行异步版本时.

代码语言:javascript
复制
PS> $testJob=Start-TestAsync -Name "My Test" -Block { ps | select -first 9 }

然后...and检查它的输出..。

代码语言:javascript
复制
PS> Receive-Job $testJob

..。它仅仅将参数引入Start函数就失败了,报告说它不能将字符串转换为ScriptBlock。因此,-Block $using:Block传递的是字符串而不是ScriptBlock!

经过一些实验,我确实找到了一个解决办法。如果我修改Start-Test,使$Block参数的类型是string而不是ScriptBlock --然后将该字符串转换回块以输入调用命令.

代码语言:javascript
复制
function Start-Test
{
    [CmdletBinding()]
    param([string]$Block, [string]$Name = '')
    $myBlock = [ScriptBlock]::Create($Block)
    Invoke-Command -ScriptBlock $myBlock
}

然后,当我从上面运行相同的命令时,我得到了正确的结果:

代码语言:javascript
复制
PS> $testJob=Start-TestAsync -Name "My Test" -Block { ps | select -first 9 }
PS> Receive-Job $testJob

在我的初始示例中,using作用域是否正常工作(将ScriptBlock转换为字符串)?关于它的有限的文档(变量范围)提供的指导很少。最终,当它的$Block参数被键入为ScriptBlock时,是否有一种方法使其开始测试工作?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2014-09-10 16:41:49

虽然知道(谢谢,@KeithHill)知道我看到的是一个已知的问题是有用的--对不起,我的意思是“设计”--我真正的问题没有得到回答(“最终,当$Block参数被输入为ScriptBlock时,是否有办法使其开始测试工作?”)

昨晚我突然想到了答案:

代码语言:javascript
复制
function Start-TestAsync
{
    [CmdletBinding()]
    param([ScriptBlock]$Block, [string]$Name = '')   
    Start-Job {
        $myBlock = [ScriptBlock]::Create($using:Block);
        Start-Test -Name $using:Name -Block $myBlock  }
}

function Start-Test
{
    [CmdletBinding()]
    param([ScriptBlock]$Block, [string]$Name = '')   
    # do some work here, including this:
    Invoke-Command -ScriptBlock $Block
}

请注意,在Start-TestAsync中,我内部允许序列化发生($using:Block),将ScriptBlock转换为字符串,然后立即将其重新转换(创建)为ScriptBlock,然后安全地将其作为真正的ScriptBlock传递到Start-Test。对我来说,这是我问题中的解决办法的一个重大改进,因为现在关于这两个函数的公共API是正确的。

票数 0
EN

Stack Overflow用户

发布于 2014-09-08 21:20:31

这显然是设计出来的:https://connect.microsoft.com/PowerShell/feedback/details/685749/passing-scriptblocks-to-the-job-as-an-argument-cannot-process-argument-transformation-on-parameter

解决方法(来自上面的链接)是使用[ScriptBlock]::Create()

之所以会发生这种情况,是因为$ScriptToNest脚本块由于PowerShell序列化的工作方式而被转换为字符串。您可以通过显式创建scriptblock来解决此问题。将$OuterScriptblock中的param()块替换为以下内容($ip是输入): scriptblock$OuterScriptblock ={ param($ip) ScriptBlock$ScriptToRun = ScriptBlock::Create($ip)

这将是你的工作(正如你所发现的):

代码语言:javascript
复制
function Start-TestAsync
{
    [CmdletBinding()]
    param([ScriptBlock]$Block, [string]$Name = '')
    Start-Job { Start-Test -Name $using:Name -Block $using:Block }
}

function Start-Test
{
    [CmdletBinding()]
    param($Block, [string]$Name = '')
    # do some work here, including this:
    $sb = [ScriptBlock]::Create($Block)
    Invoke-Command -ScriptBlock $sb
}
票数 2
EN

Stack Overflow用户

发布于 2014-09-08 20:04:06

我意识到这并不能完全回答你的问题,但我认为你可以把它简化很多,把它放在一个函数中:

代码语言:javascript
复制
function Start-Test
{
    [CmdletBinding()]
    param(
        [ScriptBlock]$Block, 
        [string]$Name = '',
        [Switch]$Async
    )
    # do some work here, including this:
    Invoke-Command -ScriptBlock $Block -AsJob:$Async
}

因为Invoke-Command已经可以为您启动一个作业,所以您可以让您的函数接受一个-Async开关,然后将其值传递给-AsJob交换机。

调用同步

代码语言:javascript
复制
Start-Test -Block { ps | select -first 9 }

调用异步

代码语言:javascript
复制
Start-Test -Block { ps | select -first 9 } -Async

炒作

至于你看到的实际情况为什么会发生,我不确定,但我认为这可能与嵌套脚本块有关,尽管我目前无法进行适当的测试。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25728616

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档