Per integrare le risposte preesistenti e utili con una guida su quando utilizzare quale approccio e un confronto delle prestazioni .
Al di fuori di una pipeline, utilizzare (PSv3 +):
$ oggetti . Nome
come dimostrato nella risposta di rageandqq , che è sia sintatticamente più semplice che molto più veloce .
In una pipeline in cui il risultato deve essere ulteriormente elaborato o i risultati non rientrano nella memoria nel suo insieme, utilizzare:
$ oggetti | Select-Object -ExpandProperty Name
- La necessità
-ExpandProperty
è spiegata nella risposta di Scott Saad .
- Ottieni i soliti vantaggi della pipeline dell'elaborazione uno a uno, che in genere produce immediatamente l'output e mantiene costante l'uso della memoria (a meno che alla fine non raccolga comunque i risultati in memoria).
- Scambio :
- L'uso della pipeline è relativamente lento .
Per raccolte di input di piccole dimensioni (array), probabilmente non noterai la differenza e, soprattutto sulla riga di comando, talvolta è più importante poter digitare facilmente il comando.
Ecco un'alternativa facile da scrivere , che tuttavia è l' approccio più lento ; utilizza una ForEach-Object
sintassi semplificata chiamata istruzione di operazione (di nuovo, PSv3 +):; ad esempio, la seguente soluzione PSv3 + è facile da aggiungere a un comando esistente:
$objects | % Name # short for: $objects | ForEach-Object -Process { $_.Name }
Per completezza: il poco noto metodo array PSv4 +.ForEach()
, più comprensibile discusso in questo articolo , è ancora un'altra alternativa :
# By property name (string):
$objects.ForEach('Name')
# By script block (more flexibility; like ForEach-Object)
$objects.ForEach({ $_.Name })
Questo approccio è simile all'enumerazione dei membri , con gli stessi compromessi, tranne per il fatto che la logica della pipeline non viene applicata; è leggermente più lento , sebbene notevolmente più veloce della conduttura.
Per estrarre un singolo valore di proprietà per nome ( argomento stringa ), questa soluzione è alla pari con l'enumerazione dei membri (sebbene quest'ultimo sia sintatticamente più semplice).
La variante a blocchi di script consente trasformazioni arbitrarie ; è un'alternativa più veloce, all-in-memory-at-once, al ForEach-Object
cmdlet basato sulla pipeline ( %
) .
Confronto delle prestazioni dei vari approcci
Ecco i tempi di campionamento per i vari approcci, basati su una raccolta di input di 10,000
oggetti , calcolati in media su 10 esecuzioni; i numeri assoluti non sono importanti e variano in base a molti fattori, ma dovrebbero darti un senso di prestazioni relative (i tempi provengono da una VM Windows 10 single-core:
Importante
Le prestazioni relative variano in base al fatto che gli oggetti di input siano istanze di normali tipi .NET (ad es. Come output di Get-ChildItem
) o [pscustomobject]
istanze (ad es. Come output di Convert-FromCsv
).
Il motivo è che le [pscustomobject]
proprietà sono gestite in modo dinamico da PowerShell e possono accedervi più rapidamente rispetto alle normali proprietà di un tipo .NET (definito staticamente) normale. Entrambi gli scenari sono trattati di seguito.
I test utilizzano come input raccolte già nella memoria piena, in modo da concentrarsi sulle prestazioni di estrazione delle proprietà pure. Con un cmdlet di streaming / chiamata di funzione come input, le differenze di prestazioni saranno generalmente molto meno pronunciate, poiché il tempo trascorso all'interno di quella chiamata può rappresentare la maggior parte del tempo trascorso.
Per brevità, alias %
viene utilizzato per il ForEach-Object
cmdlet.
Conclusioni generali , applicabili sia al tipo .NET normale che [pscustomobject]
all'input:
L'enumerazione dei membri ( $collection.Name
) e le foreach ($obj in $collection)
soluzioni sono di gran lunga le più veloci , di un fattore 10 o più veloci della soluzione basata sulla pipeline più veloce.
Sorprendentemente, si % Name
comporta molto peggio di % { $_.Name }
- vedi questo problema GitHub .
PowerShell Core supera costantemente Windows Powershell qui.
Tempi con normali tipi .NET :
- 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
conclusioni:
- In PowerShell Core ,
.ForEach('Name')
chiaramente supera .ForEach({ $_.Name })
. In Windows PowerShell, curiosamente, quest'ultimo è più veloce, anche se solo marginalmente.
Tempi con [pscustomobject]
istanze :
- 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
conclusioni:
Si noti come, con [pscustomobject]
ingresso .ForEach('Name')
dal Sorpassa gran lunga lo script blocco variante basata, .ForEach({ $_.Name })
.
Allo stesso modo, l' [pscustomobject]
input rende la pipeline Select-Object -ExpandProperty Name
più veloce, in Windows PowerShell praticamente alla pari .ForEach({ $_.Name })
, ma in PowerShell Core è ancora più lento di circa il 50%.
In breve: con la strana eccezione di % Name
, con [pscustomobject]
i metodi basati su stringhe di riferimento delle proprietà superano quelli basati su scriptblock.
Codice sorgente per i test :
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 })
. Questo può essere più conveniente da digitare alla riga di comando a volte, anche se penso che la risposta di Scott sia generalmente migliore.