Önceden var olan yararlı yanıtları, hangi yaklaşımın ne zaman kullanılacağına ve bir performans karşılaştırmasına rehberlik ederek tamamlamak .
Bir ardışık düzen dışında , (PSv3 +) kullanın:
$ nesneler . ad
rageandqq'ın cevabında gösterildiği gibi , hem sözdizimsel olarak daha basit hem de çok daha hızlı .
Sonucun daha fazla işlenmesi gereken veya sonuçların bir bütün olarak belleğe sığmadığı bir işlem hattında , şunu kullanın:
$ nesneler | Select-Object -ExpandProperty Name
- İhtiyaç
-ExpandProperty
, Scott Saad'ın cevabında açıklanıyor .
- Tek tek işlemenin olağan ardışık düzeni avantajlarını elde edersiniz, bu genellikle çıktıyı hemen üretir ve bellek kullanımını sabit tutar (sonuçta sonuçları bellekte toplamadığınız sürece).
- Takas :
- Kullanımı boru hattının nispeten olduğunu yavaş .
İçin küçük giriş koleksiyonları (diziler), muhtemelen fark olmaz bazen komutu yazın edememek, özellikle komut satırında, ve kolayca daha önemlidir.
İşte yazması kolay bir alternatif , ancak en yavaş yaklaşım ; işlem ifadesi olarak adlandırılan basitleştirilmiş ForEach-Object
sözdizimi kullanır (yine, PSv3 +):; örneğin, aşağıdaki PSv3 + çözümünün mevcut bir komuta eklenmesi kolaydır:
$objects | % Name # short for: $objects | ForEach-Object -Process { $_.Name }
Bütünlük adına: az bilinen PSv4 + .ForEach()
dizi yöntem , ele alınan daha comprehensivel Bu makalede , bir diğer bir alternatif :
# By property name (string):
$objects.ForEach('Name')
# By script block (more flexibility; like ForEach-Object)
$objects.ForEach({ $_.Name })
Bu yaklaşım üye sayım benzer olduğu boru hattı mantığı olması dışında, aynı ödünleşmeler ile değil uygulamalı; öyle marjinal yavaş hızlı boru hattı daha belirgin olsa yine de.
Tek bir özellik değeri ayıklanması için isim ( dize (ikinci sözdizimsel daha basit olsa) argüman), bu çözüm üye sayım ile eşit olduğunu.
Komut blok varyantı , keyfi sağlar dönüşümler ; bu, ardışık ForEach-Object
düzen tabanlı cmdlet ( %
) 'e göre daha hızlı - hepsi bir arada bellekte - bir alternatiftir .
Çeşitli yaklaşımların performansını karşılaştırma
Burada, 10 çalıştırmada ortalaması alınan nesnelerin girdi koleksiyonuna dayalı çeşitli yaklaşımlar için örnek zamanlamalar verilmiştir ; mutlak sayılar önemli değildir ve birçok faktöre bağlı olarak değişir, ancak size göreceli bir performans hissi vermelidir (zamanlamalar tek çekirdekli bir Windows 10 VM'den gelir:10,000
Önemli
Göreli performans, girdi nesnelerinin normal .NET Türlerinin örnekleri (örneğin, çıktı olarak Get-ChildItem
) veya [pscustomobject]
örnekler (ör Convert-FromCsv
. Çıktı olarak ) olmasına bağlı olarak değişir .
Bunun nedeni, [pscustomobject]
özelliklerin PowerShell tarafından dinamik olarak yönetilmesi ve bunlara (statik olarak tanımlanmış) normal bir .NET türünün normal özelliklerinden daha hızlı erişebilmesidir. Her iki senaryo da aşağıda ele alınmıştır.
Testler, salt özellik çıkarma performansına odaklanmak için, zaten bellekte tam olan koleksiyonları girdi olarak kullanır. Girdi olarak akışlı bir cmdlet / işlev çağrısı ile, bu çağrı içinde harcanan zaman harcanan zamanın çoğunu açıklayabileceğinden, performans farklılıkları genellikle çok daha az belirgin olacaktır.
Kısalık %
sağlamak amacıyla ForEach-Object
cmdlet için takma ad kullanılır .
Hem normal .NET türü hem de [pscustomobject]
girdisi için geçerli genel sonuçlar :
Üye numaralandırması ( $collection.Name
) ve foreach ($obj in $collection)
çözümler, en hızlı ardışık düzen tabanlı çözümden 10 kat daha hızlı veya daha hızlıdır.
Şaşırtıcı bir şekilde, % Name
şundan çok daha kötü performans gösterir % { $_.Name }
- bu GitHub sorununa bakın .
PowerShell Core, burada sürekli olarak Windows Powershell'den daha iyi performans gösterir.
Normal .NET türleriyle zamanlamalar :
- PowerShell Core v7.0.0-önizleme.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
Sonuç:
- PowerShell içinde Çekirdek ,
.ForEach('Name')
açıkça daha iyi performans .ForEach({ $_.Name })
. Windows PowerShell'de, merakla, ikincisi daha hızlıdır, ancak çok az da olsa.
[pscustomobject]
Örneklerle zamanlamalar :
- PowerShell Core v7.0.0-önizleme.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
Sonuç:
İle nasıl Not [pscustomobject]
girişi .ForEach('Name')
uzak geride tarafından komut blok varyantını esaslı, .ForEach({ $_.Name })
.
Benzer şekilde, [pscustomobject]
girdi, ardışık düzen tabanlı Select-Object -ExpandProperty Name
olanı Windows PowerShell'de neredeyse eşit .ForEach({ $_.Name })
, ancak PowerShell Core'da hala yaklaşık% 50 daha yavaş hale getirir .
Kısaca: garip haricinde % Name
olan [pscustomobject]
özelliklerini nasıl atıfta dize dayalı yöntemlerle scriptblock bazlı olanları daha iyi performans.
Testler için kaynak kodu :
Not:
$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 })
. Bu, bazen komut satırına yazmak için daha uygun olabilir, ancak bence Scott'ın cevabının genellikle daha iyi olduğunu düşünüyorum .