Pour compléter les réponses préexistantes et utiles avec des conseils sur le moment d'utiliser quelle approche et une comparaison des performances .
En dehors d'un pipeline, utilisez (PSv3 +):
$ objets . Nom
comme démontré dans la réponse de rageandqq , qui est à la fois syntaxiquement plus simple et beaucoup plus rapide .
Dans un pipeline où le résultat doit être traité plus avant ou où les résultats ne rentrent pas dans la mémoire dans son ensemble, utilisez:
$ objets | Select-Object -ExpandProperty Name
- Le besoin
-ExpandProperty
est expliqué dans la réponse de Scott Saad .
- Vous bénéficiez des avantages habituels du pipeline du traitement un par un, qui produit généralement une sortie immédiatement et maintient l'utilisation de la mémoire constante (à moins que vous ne collectiez les résultats en mémoire de toute façon).
- Compromis :
- L'utilisation du pipeline est relativement lente .
Pour les petites collections d'entrée (tableaux), vous ne remarquerez probablement pas la différence et, en particulier sur la ligne de commande, il est parfois plus important de pouvoir taper la commande facilement.
Voici une alternative facile à taper , qui est cependant l' approche la plus lente ; il utilise une ForEach-Object
syntaxe simplifiée appelée instruction d'opération (encore une fois, PSv3 +):; Par exemple, la solution PSv3 + suivante est facile à ajouter à une commande existante:
$objects | % Name # short for: $objects | ForEach-Object -Process { $_.Name }
Par souci d'exhaustivité: la méthode de tableau PSv4 +.ForEach()
peu connue , plus complète discutée dans cet article , est encore une autre alternative :
# By property name (string):
$objects.ForEach('Name')
# By script block (more flexibility; like ForEach-Object)
$objects.ForEach({ $_.Name })
Cette approche est similaire à l'énumération des membres , avec les mêmes compromis, sauf que la logique de pipeline n'est pas appliquée; il est légèrement plus lent , mais toujours sensiblement plus rapide que le pipeline.
Pour extraire une seule valeur de propriété par nom ( argument de chaîne ), cette solution est comparable à l'énumération des membres (bien que cette dernière soit syntaxiquement plus simple).
La variante de bloc de script permet des transformations arbitraires ; c'est une alternative plus rapide - tout en mémoire à la fois - à la ForEach-Object
cmdlet basée sur un pipeline ( %
) .
Comparer les performances des différentes approches
Voici des exemples de minutages pour les différentes approches, basés sur une collection d' 10,000
objets d' entrée , moyennés sur 10 exécutions; les nombres absolus ne sont pas importants et varient en fonction de nombreux facteurs, mais cela devrait vous donner une idée des performances relatives (les horaires proviennent d'une machine virtuelle Windows 10 à un seul cœur:
Important
Les performances relatives varient selon que les objets d'entrée sont des instances de types .NET normaux (par exemple, en sortie par Get-ChildItem
) ou des [pscustomobject]
instances (par exemple, en sortie par Convert-FromCsv
).
La raison est que les [pscustomobject]
propriétés sont gérées dynamiquement par PowerShell et qu'il peut y accéder plus rapidement que les propriétés normales d'un type .NET régulier (défini de manière statique). Les deux scénarios sont traités ci-dessous.
Les tests utilisent des collections déjà en mémoire dans leur intégralité comme entrée, afin de se concentrer sur les performances d'extraction de propriété pure. Avec un appel d'applet de commande / de fonction en continu comme entrée, les différences de performances seront généralement beaucoup moins prononcées, car le temps passé à l'intérieur de cet appel peut représenter la majorité du temps passé.
Par souci de concision, l'alias %
est utilisé pour l' ForEach-Object
applet de commande.
Conclusions générales , applicables à la fois au type et à l' [pscustomobject]
entrée .NET réguliers :
L'énumération des membres ( $collection.Name
) et les foreach ($obj in $collection)
solutions sont de loin les plus rapides , d'un facteur 10 ou plus que la solution basée sur le pipeline la plus rapide.
Étonnamment, les % Name
performances sont bien pires que % { $_.Name }
- voyez ce problème GitHub .
PowerShell Core surpasse constamment Windows Powershell ici.
Timings avec les types .NET normaux :
- 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
Conclusions:
- Dans PowerShell Core ,
.ForEach('Name')
surpasse clairement .ForEach({ $_.Name })
. Dans Windows PowerShell, curieusement, ce dernier est plus rapide, bien que marginalement.
Timings avec [pscustomobject]
instances :
- 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
Conclusions:
Notez comment avec [pscustomobject]
entrée .ForEach('Name')
par la variante surpasse largement basée sur un script bloc, .ForEach({ $_.Name })
.
De même, l' [pscustomobject]
entrée rend le pipeline Select-Object -ExpandProperty Name
plus rapide, dans Windows PowerShell pratiquement au même niveau que .ForEach({ $_.Name })
, mais dans PowerShell Core encore environ 50% plus lent.
En bref: à l'exception étrange de % Name
, avec [pscustomobject]
les méthodes de référencement basées sur des chaînes, les propriétés surpassent celles basées sur des blocs de script.
Code source pour les tests :
Remarque:
$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 })
. Cela peut parfois être plus pratique à taper sur la ligne de commande, même si je pense que la réponse de Scott est généralement meilleure.