【发布时间】:2019-03-29 05:37:36
【问题描述】:
我基本上是在使用运行空间构建自己的并行 foreach 管道函数。
我的问题是:我这样调用我的函数:
somePipeline | MyNewForeachFunction { scriptBlockHere } | pipelineGoesOn...
如何将$_ 参数正确传递到ScriptBlock?它在 ScriptBlock 包含作为第一行时起作用
param($_)
但您可能已经注意到,powershell 内置的 ForEach-Object 和 Where-Object 不需要在传递给它们的每个 ScriptBlock 中都需要这样的参数声明。
提前感谢您的回答 fjf2002
编辑:
目标是:我希望函数 MyNewForeachFunction 的用户感到舒适——他们不应该在他们的脚本块中写一行 param($_)。
在MyNewForeachFunction内部,目前调用的ScriptBlock是通过
$PSInstance = [powershell]::Create().AddScript($ScriptBlock).AddParameter('_', $_)
$PSInstance.BeginInvoke()
EDIT2:
关键是,例如内置函数ForEach-Object 的实现如何实现$_ 不需要在其ScriptBlock 参数中声明为参数,我也可以使用该功能吗?
(如果答案是,ForEach-Object 是一个内置函数,并且使用了一些我无法使用的魔法,那么在我看来,这将取消整个语言 PowerShell 的资格)
EDIT3:
感谢 mklement0,我终于可以构建我的通用 foreach 循环。代码如下:
function ForEachParallel {
[CmdletBinding()]
Param(
[Parameter(Mandatory)] [ScriptBlock] $ScriptBlock,
[Parameter(Mandatory=$false)] [int] $PoolSize = 20,
[Parameter(ValueFromPipeline)] $PipelineObject
)
Begin {
$RunspacePool = [runspacefactory]::CreateRunspacePool(1, $poolSize)
$RunspacePool.Open()
$Runspaces = @()
}
Process {
$PSInstance = [powershell]::Create().
AddCommand('Set-Variable').AddParameter('Name', '_').AddParameter('Value', $PipelineObject).
AddCommand('Set-Variable').AddParameter('Name', 'ErrorActionPreference').AddParameter('Value', 'Stop').
AddScript($ScriptBlock)
$PSInstance.RunspacePool = $RunspacePool
$Runspaces += New-Object PSObject -Property @{
Instance = $PSInstance
IAResult = $PSInstance.BeginInvoke()
Argument = $PipelineObject
}
}
End {
while($True) {
$completedRunspaces = @($Runspaces | where {$_.IAResult.IsCompleted})
$completedRunspaces | foreach {
Write-Output $_.Instance.EndInvoke($_.IAResult)
$_.Instance.Dispose()
}
if($completedRunspaces.Count -eq $Runspaces.Count) {
break
}
$Runspaces = @($Runspaces | where { $completedRunspaces -notcontains $_ })
Start-Sleep -Milliseconds 250
}
$RunspacePool.Close()
$RunspacePool.Dispose()
}
}
部分代码来自 MathiasR.Jessen,Why PowerShell workflow is significantly slower than non-workflow script for XML file analysis
【问题讨论】:
-
要么检查脚本块的 AST,如果不存在参数声明,则注入参数声明,或者扩展 PSCmdlet 并使用 DollarUnderscore 参数集调用脚本块
-
传递给你的脚本块的第一个参数在
$args[0]或者如果它被当作管道:$input -
@MathiasR.Jessen:你能说得更具体点吗? ForEach-Object / Where-Object 等是否也这样做?
-
@mklement0:谢谢,我已经添加了 Dispose 调用、一个理智的 ErrorActionPreference 并且我已经移除了“障碍” - 现在完成的结果会在所有运行空间之前传递到管道中已经结束了。
标签: powershell foreach pipe powershell-sdk