既存の役立つ回答を補足するために、どのアプローチを使用するかについてのガイダンスとパフォーマンスの比較を示します。
パイプラインの外では、(PSv3 +)を使用します。
$ objects 。名前
rageandqqの回答で示されているように、構文的に単純であり、はるかに高速です。
結果をさらに処理する必要があるパイプライン、または結果が全体としてメモリに収まらないパイプラインでは、以下を使用します。
$ objects | Select-Object -ExpandProperty名
- 必要性について
-ExpandProperty
は、Scott Saadの回答で説明されています。
- 1つずつ処理することで通常のパイプラインの利点が得られます。これにより、通常、すぐに出力が生成され、メモリの使用量が一定に保たれます(最終的にメモリに結果を収集しない限り)。
- トレードオフ:
以下のために小さな入力コレクション(配列)は、おそらく違いに気付くことはありません、そして、特にコマンドラインで、時には簡単にコマンドを入力できることがより重要です。
ここに入力するのは簡単ですが、これは最も遅いアプローチです。これは、操作ステートメント(ここでも、PSv3 +)と呼ばれる簡略化されたForEach-Object
構文を使用します。たとえば、次のPSv3 +ソリューションは、既存のコマンドに簡単に追加できます。
$objects | % Name # short for: $objects | ForEach-Object -Process { $_.Name }
完全を期すために、あまり知られていないPSv4 + .ForEach()
配列方式(この記事でより包括的に説明しています)は、さらに別の代替手段です。
# By property name (string):
$objects.ForEach('Name')
# By script block (more flexibility; like ForEach-Object)
$objects.ForEach({ $_.Name })
このアプローチは、パイプラインロジックが適用されないことを除いて、同じトレードオフでメンバー列挙に似ています。パイプラインよりも著しく高速ですが、わずかに遅いです。
名前(文字列引数)によって単一のプロパティ値を抽出する場合、このソリューションはメンバー列挙と同等です(後者の方が構文的に単純です)。
スクリプトブロック変異体は、任意の可能な変換を。これは、パイプラインベースのForEach-Object
コマンドレット(%
)に代わる、より高速なオールインメモリアットワンスの代替手段です。
さまざまなアプローチのパフォーマンスの比較
以下は、10回の実行にわたって平均された、オブジェクトの入力コレクションに基づく、さまざまなアプローチのサンプルタイミングです。絶対数は重要ではなく、多くの要因に基づいて変化しますが、相対的なパフォーマンスの感覚を与えるはずです(タイミングはシングルコアWindows 10 VMから取得されます:10,000
重要
相対的なパフォーマンスは、入力オブジェクトが通常の.NETタイプのインスタンス(例:による出力Get-ChildItem
)であるか[pscustomobject]
インスタンス(例:による出力)であるかによって異なりますConvert-FromCsv
。
その理由は、[pscustomobject]
プロパティはPowerShellによって動的に管理され、(静的に定義された)通常の.NETタイプの通常のプロパティよりも速くプロパティにアクセスできるためです。以下では、両方のシナリオについて説明します。
テストでは、純粋なプロパティ抽出パフォーマンスに焦点を合わせるために、すでにメモリ内に完全なコレクションを入力として使用します。ストリーミングコマンドレット/関数呼び出しを入力として使用すると、その呼び出し内で費やされた時間が費やされた時間の大半を占める可能性があるため、パフォーマンスの違いは通常、それほど顕著ではなくなります。
簡潔にするために、エイリアス%
はForEach-Object
コマンドレットに使用されます。
一般的な結論。通常の.NETタイプと[pscustomobject]
入力の両方に適用できます。
メンバーの列挙($collection.Name
)とforeach ($obj in $collection)
ソリューションは、パイプラインベースの最速のソリューションよりも10倍以上高速であり、はるかに高速です。
驚いたことに、% Name
パフォーマンスはGitHubの問題を% { $_.Name }
ご覧ください。
PowerShell Coreは、一貫してWindows Powershellよりも優れています。
通常の.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
結論:
- PowerShell Coreでは、
.ForEach('Name')
明らかにパフォーマンスが優れてい.ForEach({ $_.Name })
ます。Windows PowerShellでは、奇妙なことに、後者の方がわずかですが、高速です。
[pscustomobject]
インスタンスのタイミング:
- 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
結論:
[pscustomobject]
入力.ForEach('Name')
がスクリプトブロックベースのバリアントよりもはるかに優れていることに注意してください.ForEach({ $_.Name })
。
同様に、[pscustomobject]
入力により、Select-Object -ExpandProperty Name
実質的にWindows PowerShellと同等のパイプラインベースの処理が高速になります.ForEach({ $_.Name })
が、PowerShellコアでは依然として約50%遅くなります。
要するに:の奇数除き% Name
と、[pscustomobject]
プロパティを参照の文字列ベースの方法スクリプトブロックベースのものを上回ります。
テストのソースコード:
注意:
$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 })
。これは、コマンドラインで入力する方が便利な場合がありますが、スコットの答えの方が一般的には良いと思います。