Powershell 中的管道
Pipeline in Powershell
我在 about_Pipelines 阅读了关于管道在 PowerShell 中的工作原理,并了解到管道一次传送一个对象。
所以,这个
Get-Service | Format-Table -Property Name, DependentServices
与此不同
Format-Table -InputObject (Get-Service) -Property Name, DependentServices
所以在这里,根据解释,在第一种情况下,Format-Table
一次作用于一个对象,在第二个示例中,Format-Table
作用于一组对象。如有不妥请指正
如果是这种情况,那么我想知道 Sort-Object
和其他需要处理数据集合的 cmdlet 如何使用管道字符。
当我这样做时:
Get-Service | Sort-Object
如果一次只能处理一个对象,Sort-Object
如何进行排序。因此,假设有 100 个服务对象要传递给 Sort-Object
。 Sort-Object
会被调用 100 次吗(每次针对一个对象)?而且,这将如何产生我在屏幕上看到的排序结果。
Sort-Object
(以及其他需要在输出任何内容之前评估 all 输入对象的 cmdlet)通过一个接一个地收集输入对象,然后不执行任何操作来工作实际工作,直到上游 cmdlet(Get-Service
在本例中)完成发送输入。
这是如何工作的?好吧,让我们尝试使用 PowerShell 函数重新创建 Sort-Object
。
为此,我们首先需要了解一个 cmdlet 由 3 个独立的例程组成:
Begin
- 管道中每个 cmdlet 的 Begin
例程被调用 一次 在任何其他事情发生之前
Process
- 每个 cmdlet 每次从上游命令 接收到输入时都会调用此例程
End
- 一旦上游命令调用 End
并且 Process
没有更多的输入项要处理
(这些是 PowerShell function 定义中使用的块标签名称 - 在二进制 cmdlet 中,您将覆盖 cmdlet 的 BeginProcessing
, ProcessRecord
, EndProcessing
方法的实现)
因此,要“收集”每个输入项,我们需要在命令的 Process
块中添加一些逻辑,然后我们可以将对所有项进行操作的代码放在 End
块:
function Sort-ObjectCustom
{
param(
[Parameter(Mandatory, ValueFromPipeline)]
[object[]]$InputObject
)
begin {
# Let's use the `begin` block to create a list that'll hold all the input items
$list = [System.Collections.Generic.List[object]]::new()
Write-Verbose "Begin was called"
}
process {
# Here we simply collect all input to our list
$list.AddRange($InputObject)
Write-Verbose "Process was called [InputObject: $InputObject]"
}
end {
# The `end` block is only ever called _after_ we've collected all input
# Now we can safely sort it
$list.Sort()
Write-Verbose "End was called"
# and output the results
return $list
}
}
如果我们用 -Verbose
调用我们的新命令,我们将看到输入是如何一一收集的:
PS ~> 10..1 |Sort-ObjectCustom -Verbose
VERBOSE: Begin was called
VERBOSE: Process was called [InputObject: 10]
VERBOSE: Process was called [InputObject: 9]
VERBOSE: Process was called [InputObject: 8]
VERBOSE: Process was called [InputObject: 7]
VERBOSE: Process was called [InputObject: 6]
VERBOSE: Process was called [InputObject: 5]
VERBOSE: Process was called [InputObject: 4]
VERBOSE: Process was called [InputObject: 3]
VERBOSE: Process was called [InputObject: 2]
VERBOSE: Process was called [InputObject: 1]
VERBOSE: End was called
1
2
3
4
5
6
7
8
9
10
有关如何为二进制 cmdlet 实施管道输入处理例程的详细信息,请参阅 "How to Override Input Processing"。
有关如何在函数中利用相同管道语义的更多信息,请参阅 about_Functions_Advanced_Methods
and related help topics
为了补充 Mathias, you can actually visualize the order of the process from an existing cmdlet using the Write-Host
cmdlet 的答案,它立即将输出写入显示器(而不是管道):
$Data = ConvertFrom-Csv @'
Id, Name
4, Four
2, Two
3, Three
1, One
'@
Select-Object
例子
$Data |
Foreach-Object { Write-Host 'in:' ($_ |ConvertTo-Json -Compress); $_ } |
Select-Object * |
Foreach-Object { Write-Host 'out:' ($_ |ConvertTo-Json -Compress); $_ }
显示:
in: {"Id":"4","Name":"Four"}
out: {"Id":"4","Name":"Four"}
in: {"Id":"2","Name":"Two"}
out: {"Id":"2","Name":"Two"}
in: {"Id":"3","Name":"Three"}
out: {"Id":"3","Name":"Three"}
in: {"Id":"1","Name":"One"}
out: {"Id":"1","Name":"One"}
Id Name
-- ----
4 Four
2 Two
3 Three
1 One
Sort-Object
示例
$Data |
Foreach-Object { Write-Host 'in:' ($_ |ConvertTo-Json -Compress); $_ } |
Sort-Object * |
Foreach-Object { Write-Host 'out:' ($_ |ConvertTo-Json -Compress); $_ }
显示:
in: {"Id":"4","Name":"Four"}
in: {"Id":"2","Name":"Two"}
in: {"Id":"3","Name":"Three"}
in: {"Id":"1","Name":"One"}
out: {"Id":"1","Name":"One"}
out: {"Id":"2","Name":"Two"}
out: {"Id":"3","Name":"Three"}
out: {"Id":"4","Name":"Four"}
Id Name
-- ----
1 One
2 Two
3 Three
4 Four
一般来说,PowerShell cmdlets Write Single Records to the Pipeline where it is possible (one of the advantages of this encouraged guideline is that it reduces memory consumption). As implied by your question, Sort-Object
can't do this because the last record might possibly come before the first record. But there are also exceptions where it would be technically possible to write single records according the encouraged guideline, but it is not. See e.g.: #11221
Select-Object -Unique is unnecessary slow and exhaustive
我在 about_Pipelines 阅读了关于管道在 PowerShell 中的工作原理,并了解到管道一次传送一个对象。
所以,这个
Get-Service | Format-Table -Property Name, DependentServices
与此不同
Format-Table -InputObject (Get-Service) -Property Name, DependentServices
所以在这里,根据解释,在第一种情况下,Format-Table
一次作用于一个对象,在第二个示例中,Format-Table
作用于一组对象。如有不妥请指正
如果是这种情况,那么我想知道 Sort-Object
和其他需要处理数据集合的 cmdlet 如何使用管道字符。
当我这样做时:
Get-Service | Sort-Object
如果一次只能处理一个对象,Sort-Object
如何进行排序。因此,假设有 100 个服务对象要传递给 Sort-Object
。 Sort-Object
会被调用 100 次吗(每次针对一个对象)?而且,这将如何产生我在屏幕上看到的排序结果。
Sort-Object
(以及其他需要在输出任何内容之前评估 all 输入对象的 cmdlet)通过一个接一个地收集输入对象,然后不执行任何操作来工作实际工作,直到上游 cmdlet(Get-Service
在本例中)完成发送输入。
这是如何工作的?好吧,让我们尝试使用 PowerShell 函数重新创建 Sort-Object
。
为此,我们首先需要了解一个 cmdlet 由 3 个独立的例程组成:
Begin
- 管道中每个 cmdlet 的Begin
例程被调用 一次 在任何其他事情发生之前Process
- 每个 cmdlet 每次从上游命令 接收到输入时都会调用此例程
End
- 一旦上游命令调用End
并且Process
没有更多的输入项要处理
(这些是 PowerShell function 定义中使用的块标签名称 - 在二进制 cmdlet 中,您将覆盖 cmdlet 的 BeginProcessing
, ProcessRecord
, EndProcessing
方法的实现)
因此,要“收集”每个输入项,我们需要在命令的 Process
块中添加一些逻辑,然后我们可以将对所有项进行操作的代码放在 End
块:
function Sort-ObjectCustom
{
param(
[Parameter(Mandatory, ValueFromPipeline)]
[object[]]$InputObject
)
begin {
# Let's use the `begin` block to create a list that'll hold all the input items
$list = [System.Collections.Generic.List[object]]::new()
Write-Verbose "Begin was called"
}
process {
# Here we simply collect all input to our list
$list.AddRange($InputObject)
Write-Verbose "Process was called [InputObject: $InputObject]"
}
end {
# The `end` block is only ever called _after_ we've collected all input
# Now we can safely sort it
$list.Sort()
Write-Verbose "End was called"
# and output the results
return $list
}
}
如果我们用 -Verbose
调用我们的新命令,我们将看到输入是如何一一收集的:
PS ~> 10..1 |Sort-ObjectCustom -Verbose
VERBOSE: Begin was called
VERBOSE: Process was called [InputObject: 10]
VERBOSE: Process was called [InputObject: 9]
VERBOSE: Process was called [InputObject: 8]
VERBOSE: Process was called [InputObject: 7]
VERBOSE: Process was called [InputObject: 6]
VERBOSE: Process was called [InputObject: 5]
VERBOSE: Process was called [InputObject: 4]
VERBOSE: Process was called [InputObject: 3]
VERBOSE: Process was called [InputObject: 2]
VERBOSE: Process was called [InputObject: 1]
VERBOSE: End was called
1
2
3
4
5
6
7
8
9
10
有关如何为二进制 cmdlet 实施管道输入处理例程的详细信息,请参阅 "How to Override Input Processing"。
有关如何在函数中利用相同管道语义的更多信息,请参阅 about_Functions_Advanced_Methods
and related help topics
为了补充 Mathias, you can actually visualize the order of the process from an existing cmdlet using the Write-Host
cmdlet 的答案,它立即将输出写入显示器(而不是管道):
$Data = ConvertFrom-Csv @'
Id, Name
4, Four
2, Two
3, Three
1, One
'@
Select-Object
例子
$Data |
Foreach-Object { Write-Host 'in:' ($_ |ConvertTo-Json -Compress); $_ } |
Select-Object * |
Foreach-Object { Write-Host 'out:' ($_ |ConvertTo-Json -Compress); $_ }
显示:
in: {"Id":"4","Name":"Four"}
out: {"Id":"4","Name":"Four"}
in: {"Id":"2","Name":"Two"}
out: {"Id":"2","Name":"Two"}
in: {"Id":"3","Name":"Three"}
out: {"Id":"3","Name":"Three"}
in: {"Id":"1","Name":"One"}
out: {"Id":"1","Name":"One"}
Id Name
-- ----
4 Four
2 Two
3 Three
1 One
Sort-Object
示例
$Data |
Foreach-Object { Write-Host 'in:' ($_ |ConvertTo-Json -Compress); $_ } |
Sort-Object * |
Foreach-Object { Write-Host 'out:' ($_ |ConvertTo-Json -Compress); $_ }
显示:
in: {"Id":"4","Name":"Four"}
in: {"Id":"2","Name":"Two"}
in: {"Id":"3","Name":"Three"}
in: {"Id":"1","Name":"One"}
out: {"Id":"1","Name":"One"}
out: {"Id":"2","Name":"Two"}
out: {"Id":"3","Name":"Three"}
out: {"Id":"4","Name":"Four"}
Id Name
-- ----
1 One
2 Two
3 Three
4 Four
一般来说,PowerShell cmdlets Write Single Records to the Pipeline where it is possible (one of the advantages of this encouraged guideline is that it reduces memory consumption). As implied by your question, Sort-Object
can't do this because the last record might possibly come before the first record. But there are also exceptions where it would be technically possible to write single records according the encouraged guideline, but it is not. See e.g.: #11221
Select-Object -Unique is unnecessary slow and exhaustive