Para complementar as respostas preexistentes e úteis, com orientações sobre quando usar qual abordagem e uma comparação de desempenho .
Fora de um pipeline, use (PSv3 +):
$ objetos . Nome
como demonstrado na resposta de rageandqq , que é sintaticamente mais simples e muito mais rápida .
Em um pipeline em que o resultado deve ser processado posteriormente ou os resultados não cabem na memória como um todo, use:
objetos $ | Nome do objeto Select -ExpandProperty
- A necessidade
-ExpandProperty
é explicada na resposta de Scott Saad .
- Você obtém os benefícios usuais do pipeline do processamento um por um, que normalmente produz saída imediatamente e mantém o uso da memória constante (a menos que você finalmente colete os resultados na memória).
- Tradeoff :
- O uso do pipeline é comparativamente lento .
Para pequenas coleções de entradas (matrizes), você provavelmente não notará a diferença e, especialmente na linha de comando, às vezes é possível digitar o comando com facilidade.
Aqui está uma alternativa fácil de digitar , que, no entanto, é a abordagem mais lenta ; usa sintaxe simplificada ForEach-Object
chamada instrução de operação (novamente, PSv3 +):; por exemplo, é fácil anexar a seguinte solução PSv3 + a um comando existente:
$objects | % Name # short for: $objects | ForEach-Object -Process { $_.Name }
Por uma questão de completude: O pouco conhecido método de array PSv4 +.ForEach()
, mais abrangente discutido neste artigo , é outra alternativa :
# By property name (string):
$objects.ForEach('Name')
# By script block (more flexibility; like ForEach-Object)
$objects.ForEach({ $_.Name })
Essa abordagem é semelhante à enumeração de membros , com as mesmas vantagens, exceto que a lógica do pipeline não é aplicada; é marginalmente mais lento , embora ainda visivelmente mais rápido que o oleoduto.
Para extrair um único valor de propriedade por nome ( argumento de seqüência de caracteres ), esta solução está em pé de igualdade com a enumeração de membros (embora a última seja sintaticamente mais simples).
A variante de bloco de script , permite transformações arbitrárias ; é uma alternativa mais rápida - tudo na memória de uma só vez - ao ForEach-Object
cmdlet baseado em pipeline ( %
) .
Comparando o desempenho das várias abordagens
Aqui estão exemplos de tempos para as várias abordagens, com base em uma coleção de 10,000
objetos de entrada , com média de 10 execuções; os números absolutos não são importantes e variam com base em muitos fatores, mas devem fornecer uma sensação de desempenho relativo (os tempos vêm de uma VM do Windows 10 de núcleo único:
Importante
O desempenho relativo varia de acordo com se os objetos de entrada são instâncias de tipos .NET regulares (por exemplo, como saída por Get-ChildItem
) ou [pscustomobject]
instâncias (por exemplo, como saída por Convert-FromCsv
).
O motivo é que as [pscustomobject]
propriedades são gerenciadas dinamicamente pelo PowerShell e podem acessá-las mais rapidamente do que as propriedades regulares de um tipo .NET regular (definido estaticamente). Ambos os cenários são abordados abaixo.
Os testes usam coleções já com memória na íntegra como entrada, para focar no desempenho de extração de propriedade pura. Com um cmdlet de streaming / chamada de função como entrada, as diferenças de desempenho geralmente serão muito menos pronunciadas, pois o tempo gasto dentro dessa chamada pode ser responsável pela maior parte do tempo gasto.
Por questões de brevidade, o alias %
é usado para o ForEach-Object
cmdlet.
Conclusões gerais , aplicáveis ao tipo e [pscustomobject]
entrada regulares do .NET :
A enumeração de membros ( $collection.Name
) e as foreach ($obj in $collection)
soluções são de longe as mais rápidas , por um fator 10 ou mais rápido que a solução mais rápida baseada em pipeline.
Surpreendentemente, tem um % Name
desempenho muito pior do que % { $_.Name }
- veja este problema do GitHub .
O PowerShell Core supera consistentemente o Windows Powershell aqui.
Horários com tipos .NET regulares :
- PowerShell Core v7.0.0-preview.3
Factor Command Secs (10-run avg.)
------ ------- ------------------
1.00 $objects.Name 0.005
1.06 foreach($o in $objects) { $o.Name } 0.005
6.25 $objects.ForEach('Name') 0.028
10.22 $objects.ForEach({ $_.Name }) 0.046
17.52 $objects | % { $_.Name } 0.079
30.97 $objects | Select-Object -ExpandProperty Name 0.140
32.76 $objects | % Name 0.148
- Windows PowerShell v5.1.18362.145
Comparing property-value extraction methods with 10000 input objects, averaged over 10 runs...
Factor Command Secs (10-run avg.)
------ ------- ------------------
1.00 $objects.Name 0.012
1.32 foreach($o in $objects) { $o.Name } 0.015
9.07 $objects.ForEach({ $_.Name }) 0.105
10.30 $objects.ForEach('Name') 0.119
12.70 $objects | % { $_.Name } 0.147
27.04 $objects | % Name 0.312
29.70 $objects | Select-Object -ExpandProperty Name 0.343
Conclusões:
- No PowerShell Core ,
.ForEach('Name')
supera claramente o desempenho .ForEach({ $_.Name })
. Curiosamente, no Windows PowerShell, o último é mais rápido, embora apenas marginalmente.
Tempos com [pscustomobject]
instâncias :
- PowerShell Core v7.0.0-preview.3
Factor Command Secs (10-run avg.)
------ ------- ------------------
1.00 $objects.Name 0.006
1.11 foreach($o in $objects) { $o.Name } 0.007
1.52 $objects.ForEach('Name') 0.009
6.11 $objects.ForEach({ $_.Name }) 0.038
9.47 $objects | Select-Object -ExpandProperty Name 0.058
10.29 $objects | % { $_.Name } 0.063
29.77 $objects | % Name 0.184
- Windows PowerShell v5.1.18362.145
Factor Command Secs (10-run avg.)
------ ------- ------------------
1.00 $objects.Name 0.008
1.14 foreach($o in $objects) { $o.Name } 0.009
1.76 $objects.ForEach('Name') 0.015
10.36 $objects | Select-Object -ExpandProperty Name 0.085
11.18 $objects.ForEach({ $_.Name }) 0.092
16.79 $objects | % { $_.Name } 0.138
61.14 $objects | % Name 0.503
Conclusões:
Observe como a [pscustomobject]
entrada .ForEach('Name')
supera de longe a variante baseada em bloco de script .ForEach({ $_.Name })
,.
Da mesma forma, a [pscustomobject]
entrada torna o pipeline Select-Object -ExpandProperty Name
mais rápido, no Windows PowerShell praticamente igual .ForEach({ $_.Name })
, mas no PowerShell Core ainda é 50% mais lento.
Resumindo: com a exceção estranha de % Name
, com [pscustomobject]
os métodos baseados em string de referência, as propriedades superam as baseadas em scriptblock.
Código fonte dos testes :
Nota:
$count = 1e4 # max. input object count == 10,000
$runs = 10 # number of runs to average
# Note: Using [pscustomobject] instances rather than instances of
# regular .NET types changes the performance characteristics.
# Set this to $true to test with [pscustomobject] instances below.
$useCustomObjectInput = $false
# Create sample input objects.
if ($useCustomObjectInput) {
# Use [pscustomobject] instances.
$objects = 1..$count | % { [pscustomobject] @{ Name = "$foobar_$_"; Other1 = 1; Other2 = 2; Other3 = 3; Other4 = 4 } }
} else {
# Use instances of a regular .NET type.
# Note: The actual count of files and folders in your home dir. tree
# may be less than $count
$objects = Get-ChildItem -Recurse $HOME | Select-Object -First $count
}
Write-Host "Comparing property-value extraction methods with $($objects.Count) input objects, averaged over $runs runs..."
# An array of script blocks with the various approaches.
$approaches = { $objects | Select-Object -ExpandProperty Name },
{ $objects | % Name },
{ $objects | % { $_.Name } },
{ $objects.ForEach('Name') },
{ $objects.ForEach({ $_.Name }) },
{ $objects.Name },
{ foreach($o in $objects) { $o.Name } }
# Time the approaches and sort them by execution time (fastest first):
Time-Command $approaches -Count $runs | Select Factor, Command, Secs*
$results = @($objects | %{ $_.Name })
. Às vezes, pode ser mais conveniente digitar na linha de comando, embora eu ache a resposta de Scott geralmente melhor.