Uzupełnienie istniejących, pomocnych odpowiedzi wraz ze wskazówkami, kiedy zastosować które podejście, oraz porównaniem wyników .
Poza rurociągiem użyj (PSv3 +):
$ obiektów . Nazwa
jak pokazano w odpowiedzi rageandqq , która jest zarówno prostsza składniowo, jak i znacznie szybsza .
W potoku, w którym wynik musi być dalej przetwarzany lub wyniki nie mieszczą się w całości w pamięci, użyj:
$ obiektów | Select-Object -ExpandProperty Name
- Potrzeba
-ExpandProperty
jest wyjaśniona w odpowiedzi Scotta Saada .
- Otrzymujesz zwykłe zalety potoku przetwarzania jeden po drugim, które zwykle generuje dane wyjściowe od razu i utrzymuje stałe użycie pamięci (chyba że ostatecznie i tak zbierzesz wyniki w pamięci).
- Kompromis :
- Wykorzystanie rurociągu jest stosunkowo powolne .
W przypadku małych kolekcji danych wejściowych (tablic) prawdopodobnie nie zauważysz różnicy , a zwłaszcza w wierszu poleceń, czasami łatwiejsze wpisanie polecenia jest ważniejsze.
Oto łatwa do wpisania alternatywa , która jest jednak najwolniejszym podejściem ; używa uproszczonej ForEach-Object
składni zwanej instrukcją operacji (znowu PSv3 +):; np. następujące rozwiązanie PSv3 + można łatwo dodać do istniejącego polecenia:
$objects | % Name
Gwoli ścisłości.ForEach()
: Inną alternatywą jest mało znana metoda tablicowa PSv4 + , bardziej wszechstronna omówiona w tym artykule :
$objects.ForEach('Name')
$objects.ForEach({ $_.Name })
To podejście jest podobne do wyliczania elementów członkowskich , z tymi samymi kompromisami, z wyjątkiem tego, że logika potoków nie jest stosowana; jest nieznacznie wolniejszy , choć nadal zauważalnie szybszy niż rurociąg.
W przypadku wyodrębniania pojedynczej wartości właściwości według nazwy ( argument ciągu ) to rozwiązanie jest równe wyliczaniu elementów członkowskich (chociaż to drugie jest prostsze składniowo).
Skrypt-block wariant umożliwia arbitralne transformacji ; jest to szybsza alternatywa dla polecenia ForEach-Object
cmdlet ( %
) opartego na potokach .
Porównanie wydajności różnych podejść
Oto przykładowe czasy dla różnych podejść, oparte na wejściowej kolekcji 10,000
obiektów , uśrednione z 10 przebiegów; liczby bezwzględne nie są ważne i różnią się w zależności od wielu czynników, ale powinny dać poczucie względnej wydajności (czasy pochodzą z jednordzeniowej maszyny wirtualnej z systemem Windows 10:
Ważny
Względna wydajność różni się w zależności od tego, czy obiekty wejściowe są instancjami zwykłych typów .NET (np. Jako dane wyjściowe przez Get-ChildItem
), czy [pscustomobject]
instancjami (np. Jako dane wyjściowe przez Convert-FromCsv
).
Powodem jest to, że [pscustomobject]
właściwości są dynamicznie zarządzane przez PowerShell i może uzyskać do nich dostęp szybciej niż zwykłe właściwości (zdefiniowanego statycznie) zwykłego typu .NET. Oba scenariusze omówiono poniżej.
Testy używają kolekcji już znajdujących się w pamięci jako danych wejściowych, aby skupić się na wydajności wyodrębniania czystej właściwości. W przypadku przesyłania strumieniowego polecenia cmdlet / wywołania funkcji jako danych wejściowych różnice w wydajności będą na ogół znacznie mniej wyraźne, ponieważ czas spędzony wewnątrz tego wywołania może stanowić większość spędzonego czasu.
Dla zwięzłości alias %
jest używany w poleceniu ForEach-Object
cmdlet.
Ogólne wnioski , dotyczące zarówno zwykłego typu .NET, jak i [pscustomobject]
danych wejściowych:
Wyliczanie elementów członkowskich ( $collection.Name
) i foreach ($obj in $collection)
rozwiązania są zdecydowanie najszybsze , o współczynnik 10 lub więcej szybciej niż najszybsze rozwiązanie oparte na potokach.
O dziwo, % Name
działa znacznie gorzej niż % { $_.Name }
- zobacz ten numer GitHub .
PowerShell Core konsekwentnie przewyższa tutaj Windows Powershell.
Czasy ze zwykłymi typami .NET :
- PowerShell Core v7.0.0 - wersja zapoznawcza 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 w wersji 5.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
Wnioski:
- W PowerShell Rdzenia ,
.ForEach('Name')
wyraźnie przewyższa .ForEach({ $_.Name })
. Co ciekawe, w programie Windows PowerShell ten drugi jest szybszy, choć tylko nieznacznie.
Czasy z [pscustomobject]
instancjami :
- PowerShell Core v7.0.0 - wersja zapoznawcza 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 w wersji 5.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
Wnioski:
Uwaga jak z [pscustomobject]
wejściem .ForEach('Name')
zdecydowanie przewyższa skrypt blok oparty wariantu .ForEach({ $_.Name })
.
Podobnie [pscustomobject]
dane wejściowe sprawiają , że proces oparty na potoku jest Select-Object -ExpandProperty Name
szybszy, w programie Windows PowerShell praktycznie na równi z .ForEach({ $_.Name })
, ale w programie PowerShell Core nadal o około 50% wolniejszy.
W skrócie: z dziwnym wyjątkiem % Name
, [pscustomobject]
metody odwoływania się do właściwości oparte na ciągach znaków przewyższają te oparte na skryptach.
Kod źródłowy do testów :
Uwaga:
Pobierz funkcję Time-Command
z tego streszczenia, aby uruchomić te testy.
Zamiast tego ustaw $useCustomObjectInput
na $true
pomiar z [pscustomobject]
instancjami.
$count = 1e4
$runs = 10
$useCustomObjectInput = $false
if ($useCustomObjectInput) {
$objects = 1..$count | % { [pscustomobject] @{ Name = "$foobar_$_"; Other1 = 1; Other2 = 2; Other3 = 3; Other4 = 4 } }
} else {
$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..."
$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-Command $approaches -Count $runs | Select Factor, Command, Secs*
$results = @($objects | %{ $_.Name })
. Czasami wygodniej jest pisać w wierszu poleceń, chociaż myślę, że odpowiedź Scotta jest generalnie lepsza.