我经常编写需要接受管道的函数,如:
function Invoke-ExchangeHubChecks
{
[cmdletbinding()]
param
(
[Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
[object[]]$oServersToTest,
[int]$QueueWarnThreshold = 50,
[int]$QueueFailThreshold = 100
)
begin
{
$oHubTests = @()
$c=0
}
Process
{
$c++
$Server = $oServersToTest[0]
$oHubTests += [pscustomobject]@{
Identity = $server.Identity
Queue = "blah"
Count = $c
}
}
end
{
$oHubTests
}
}
end
{
$oHubTests
}
}
我经常在进程块中看到带有foreach的代码片段。我的理解是,过程块替换了对foreach块的需求(在大多数情况下)。
我需要在输入对象“$ oServersToTest”中添加一个“过滤器”。我使用“where-object”来创建过滤器:
$Server = $oServersToTest[0] | Where-Object {$_.IsHub}
但是,当迭代集合时,它会将与where子句不匹配的对象添加到自定义对象,由$ c证明:
输出:
Identity Queue Count
-------- ----- -----
Server01 blah 1
Server02 blah 2
Server03 blah 3
Server04 blah 4
Server05 blah 5
blah 6
blah 7
Server06 blah 8
Server07 blah 9
Server08 blah 10
Server09 blah 11
Server10 blah 12
blah 13
blah 14
这是一个我必须在进程块中使用foreach的示例:
Process
{
$c++
#$Server = $oServersToTest[0] | Where-Object{$_.IsHubServer}
$TestResult = @()
$oServersToTest | Where-Object {$_.IsHubServer} | Foreach-object{
$TestResult += [pscustomobject]@{
Identity = $_.Identity
Queue = "blah"
Count = $c
}
}
$oHubTests += $TestResult
}
End
{
$oHubTests
}
哪个有效,但为什么这样做而不是其他方式?对我来说,它正在做两倍的工作。
(Powershell 5.0)
TIA
答案 0 :(得分:1)
我会在foreach()
块中使用process
循环。
作为Jeff Zeitlin points out,这也允许您支持使用命名参数和流水线操作来处理集合。
使用foreach()
超过ForEach-Object
的原因有三个:
ForEach-Object
慢于foreach()
$Server
),使脚本更具可读性,避免与循环中嵌套的ForEach-Object
或Where-Object
子句混淆foreach()
,您可以免费获得break
和continue
等快速流量控制选项。process {
$oHubTests += foreach($server in $oServersToTest |Where-Object {$_.IsHubServer}){
$c++
[pscustomobject]@{
Identity = $server.Identity
Queue = "blah"
Count = $c
}
}
}
如果你的cmdlet唯一做的就是接受输入并构造相应的[pscustomobject]
,那么我会完全删除$oHubTests
部分,并在它们流动时输出对象通过:
function Invoke-ExchangeHubChecks
{
[CmdletBinding()]
param
(
[Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
[object[]]$oServersToTest,
[int]$QueueWarnThreshold = 50,
[int]$QueueFailThreshold = 100
)
begin {
$c=0
}
process {
foreach($server in $oServersToTest |Where-Object {$_.IsHubServer}){
$c++
[pscustomobject]@{
Identity = $server.Identity
Queue = "blah"
Count = $c
}
}
}
}
答案 1 :(得分:1)
您正在为我解决PowerShell最复杂的设计结构之一。创建一个cmdlet,该cmdlet可以处理作为参数提供的对象数组或每个通过管道传输到cmdlet中的项目。
例如do-something -input $items
或$items | do-something
。
进程块主要用于cmdlet接受管道值时。它并不像你想象的那样取代for循环。管道已经对集合进行了隐式循环,尽管这不是100%准确,但这是一个不同的故事。为简单起见,我将继续这种不准确的陈述。
在这种情况下,begin
,process
和end
允许您控制管道或虚构循环的每个部分发生的情况。 Before
和end
仅执行一次,可用于初始化和完成。在上面的示例中,第一个begin
已执行,然后process
为从集合中传输的每个项目,然后end
。但是,如果该集合被用作普通参数,那么它的begin
,process
内部有一个循环,然后是end
。
我理解这很复杂但是尝试使用do-something
实现进行可视化,该实现仅向主机写入正在进行的操作。
在开发MarkdownPS时我必须理解这个顺序。检查此example以及根目录上的使用示例。 Get-ChildItem | Select-Object Name | New-MDList
。仅供参考,最后一个例子是我在这个答案中使用的不准确陈述的演示。