select-object和在同一个对象上使用foreach之间的区别

时间:2014-10-11 14:31:59

标签: powershell

有人可以帮我理解下面两段代码之间的区别。为什么两者的结果都不同。我在每种情况下选择相同的属性(名称):

代码1:

$obj = Get-Service | Where-Object {$_.Status -eq "Running"} | foreach-object  {$_.Name} | select -first 3
foreach ( $item in $obj ) { write-output "Name is : $item" }
Output :
Name is : AeLookupSvc
Name is : Appinfo
Name is : AudioEndpointBuilder

代码2:

$obj = Get-Service | Where-Object {$_.Status -eq "Running"} | select -first 3 name
foreach ( $item in $obj ) { write-output "Name is : $item" }
Output :
Name is : @{Name=AeLookupSvc}
Name is : @{Name=Appinfo}
Name is : @{Name=AudioEndpointBuilder}

3 个答案:

答案 0 :(得分:11)

Select-Object将一个自定义对象数组返回给管道;在这种情况下,只有一个属性恰好是一个字符串 正如@walidtourni所提到的,使用expand可以解决这个问题。这是因为expand导致输出为属性的值,而不是具有该值的属性的自定义对象。这是可能的原因是expand只需要一个参数;即,您不可能尝试为同一“行”返回多个值。

另一方面,foreach-object只是将东西吐出到管道中。如果您尝试包含第二个属性而不手动将两者都包装到自定义对象中,则输出将在同一行上创建另一行而不是两个属性。

要演示,请运行以下命令:

Clear
$x = Get-Service | Where-Object {$_.Status -eq "Running"}  | select -first 3 
$x | foreach-object  {$_.Name}  #returns 3 rows, single column; string
$x | foreach-object  {$_.Name,$_.CanPauseAndContinue;} #returns 6 rows, single column; alternate string & boolean
$x | foreach-object  {$_.Name;$_.CanPauseAndContinue;} #returns 6 rows, single column; alternate string & boolean
$x | select Name #returns 3 rows, single column (string); custom object
$x | select Name, CanPauseAndContinue #returns 3 rows, two columns (string & boolean); custom property
$x | select -expand Name #returns 3 rows, single column; string; notice the lack of column header showing this is a string, not a string property of a custom object
$x | select -expand Name,CanPauseAndContinue #error; -expand can only take single valued arguments
$x | select -expand Name -expand CanPauseAndContinue #error; you can't try to define the same paramenter twice

答案 1 :(得分:3)

对于类似的结果,您可以更改第二个示例:

select -first 3 -expand name

选择对象选择属性对象

答案 2 :(得分:0)

这是一篇旧帖子,但我想我会扩展@JohnLBevan 的答案。

Select-Object 的正常操作是创建一个 PSCustomObject 来复制原始对象的属性。

Get-Process
Get-Process | Select-Object Name,CPU

如果您只指定一个属性,那么它会创建一个只有一个属性 (Get-Process | Select-Object Name) 的对象——这就是您在 @{Name=AeLookupSvc} 等中看到的。请注意,这与属性的值完全不同,它有一个支持获取的参数:

Get-Process | Select-Object -ExpandProperty Name
另一方面,

ForEach-Object 的作用更像是针对每个管道对象执行的循环体。这很容易用于检索要传递的属性的值,甚至还提供了一种方便的简写方式。

Get-Process | ForEach-Object Name

所以为了真正补充其他答案,我对性能差异做了一些探索:

# Benchmark
1..100 | ForEach-Object {Measure-Command {Get-WinEvent -LogName System -ErrorAction SilentlyContinue} | Select-Object -ExpandProperty TotalMinutes} | Measure-Object -Average
# Average: 2.01325857505 Minutes

# Select-Object -ExpandProperty
1..100 | ForEach-Object {Measure-Command {Get-WinEvent -LogName System -ErrorAction SilentlyContinue | Select-Object -ExpandProperty LevelDisplayName -ErrorAction SilentlyContinue} | Select-Object -ExpandProperty TotalMinutes} | Measure-Object -Average
# Average: 2.87394915385 Minutes
# note the `Select-Object -ErrorAction SilentlyContinue` because not all pipeline records are accepted by Select-Object (strange, but I did not explore why)

# ForEach-Object
1..100 | ForEach-Object {Measure-Command {Get-WinEvent -LogName System -ErrorAction SilentlyContinue | ForEach-Object LevelDisplayName} | Select-Object -ExpandProperty TotalMinutes} | Measure-Object -Average
# Average: 3.10188571238333 Minutes

请注意,我在 PowerShell 5.1 上并行运行这些(不同的 PowerShell 窗口)。