我不想用这个问题听起来太可爱,但这确实是眼前的问题。考虑在安装在PowerShell下的Test.psm1模块Test.psm1中定义的以下两个函数:
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
}导入模块后,我可以运行同步函数.
PS> Start-Test -Name "My Test" -Block { ps | select -first 9 }...and显示来自Get进程的适当输出。
但是,当我试图运行异步版本时.
PS> $testJob=Start-TestAsync -Name "My Test" -Block { ps | select -first 9 }然后...and检查它的输出..。
PS> Receive-Job $testJob..。它仅仅将参数引入Start函数就失败了,报告说它不能将字符串转换为ScriptBlock。因此,-Block $using:Block传递的是字符串而不是ScriptBlock!
经过一些实验,我确实找到了一个解决办法。如果我修改Start-Test,使$Block参数的类型是string而不是ScriptBlock --然后将该字符串转换回块以输入调用命令.
function Start-Test
{
[CmdletBinding()]
param([string]$Block, [string]$Name = '')
$myBlock = [ScriptBlock]::Create($Block)
Invoke-Command -ScriptBlock $myBlock
}然后,当我从上面运行相同的命令时,我得到了正确的结果:
PS> $testJob=Start-TestAsync -Name "My Test" -Block { ps | select -first 9 }
PS> Receive-Job $testJob在我的初始示例中,using作用域是否正常工作(将ScriptBlock转换为字符串)?关于它的有限的文档(变量,范围)提供的指导很少。最终,当它的$Block参数被键入为ScriptBlock时,是否有一种方法使其开始测试工作?
发布于 2014-09-10 16:41:49
虽然知道(谢谢,@KeithHill)知道我看到的是一个已知的问题是有用的--对不起,我的意思是“设计”--我真正的问题没有得到回答(“最终,当$Block参数被输入为ScriptBlock时,是否有办法使其开始测试工作?”)
昨晚我突然想到了答案:
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是正确的。
发布于 2014-09-08 21:20:31
解决方法(来自上面的链接)是使用[ScriptBlock]::Create()
之所以会发生这种情况,是因为$ScriptToNest脚本块由于PowerShell序列化的工作方式而被转换为字符串。您可以通过显式创建scriptblock来解决此问题。将$OuterScriptblock中的param()块替换为以下内容($ip是输入): scriptblock$OuterScriptblock ={ param($ip) ScriptBlock$ScriptToRun = ScriptBlock::Create($ip)
这将是你的工作(正如你所发现的):
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
}发布于 2014-09-08 20:04:06
我意识到这并不能完全回答你的问题,但我认为你可以把它简化很多,把它放在一个函数中:
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交换机。
调用同步
Start-Test -Block { ps | select -first 9 }调用异步
Start-Test -Block { ps | select -first 9 } -Async炒作
至于你看到的实际情况为什么会发生,我不确定,但我认为这可能与嵌套脚本块有关,尽管我目前无法进行适当的测试。
https://stackoverflow.com/questions/25728616
复制相似问题