我并不是说这个问题听起来太可爱了,但这确实是手头的问题。考虑在$ env下安装的PowerShell模块Test.psm1中定义的以下两个函数:PSModulePath:
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 }
...并显示Get-Process的适当输出。
但是,当我尝试运行异步版本时......
PS> $testJob=Start-TestAsync -Name "My Test" -Block { ps | select -first 9 }
...然后查看其输出......
PS> Receive-Job $testJob
...它只是将参数引入Start-Test函数失败,报告它无法将String转换为ScriptBlock。因此,-Block $using:Block
传递的是String而不是ScriptBlock!
经过一些实验,我确实找到了解决方法。如果我修改Start-Test以使$ Block参数的类型为[string]而不是[ScriptBlock] - 然后将该字符串转换回块以提供给Invoke-Command ......
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转换为字符串)?关于它的有限文档(about_Remote_Variables,about_Scopes)几乎没有提供任何指导。
最终,当$ Block参数被输入为[ScriptBlock]时,有没有办法让Start-Test工作?
答案 0 :(得分:2)
解决方法(来自上面的链接)是使用[ScriptBlock]::Create()
:
这是因为$ ScriptToNest脚本块被转换为字符串 因为PowerShell序列化的工作原理。您可以明确地解决这个问题 创建脚本块。用$ 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
}
答案 1 :(得分:0)
我意识到这并没有完全回答你的问题,但我认为你可以通过把它放到一个函数中来简化这个:
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
至于为什么你看到的实际情况正在发生,我不确定,但我认为这可能与嵌套脚本块有关,尽管我现在无法进行适当的测试。
答案 2 :(得分:0)
我相信你看到这个的原因是$ using的目的是在远程系统上使用之前扩展脚本块中局部变量的值 - 它实际上并没有在远程会话中创建这些变量。 scriptblock与值最接近的是它的命令文本。
答案 3 :(得分:0)
虽然知道(感谢@KeithHill)我看到的是一个已知问题很有用 - 对不起,我的意思是&#34;设计&#34; - 我真正的问题没有得到解答(< em>&#34;最终,当$ Block参数被输入为[ScriptBlock]时,有没有办法让Start-Test工作?&#34; )
昨晚突然听到了答案:
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转换为String,然后立即将其重新转换(Create)为ScriptBlock,然后可以安全地传递它作为一个真正的ScriptBlock开始测试。 对我来说,这是对我的问题中的解决方法的重大改进,因为现在两个函数的公共API都是正确的。