Чтобы дополнить уже существующие полезные ответы, указав, когда какой подход использовать, и сравнением производительности .
Вне конвейера используйте (PSv3 +):
$ объекты . название
как показано в ответе rageandqq , который синтаксически проще и намного быстрее .
В конвейере, где результат должен быть обработан дальше или результаты не помещаются в память в целом, используйте:
$ объекты | Select-Object -ExpandProperty Name
- Необходимость
-ExpandProperty
объясняется в ответе Скотта Саада .
- Вы получаете обычные преимущества конвейерной обработки поочередной обработки, которая обычно производит вывод сразу и сохраняет постоянное использование памяти (если вы в конечном итоге не собираете результаты в памяти).
- Компромисс :
- Использование трубопровода сравнительно медленно .
Для небольших входных коллекций (массивов) вы, вероятно, не заметите разницы , и, особенно в командной строке, иногда более важно иметь возможность легко ввести команду.
Вот альтернатива , которую легко набрать , но это самый медленный подход ; он использует упрощенный ForEach-Object
синтаксис, называемый оператором операции (опять же, PSv3 +):; например, следующее решение PSv3 + легко добавить к существующей команде:
$objects | % Name # short for: $objects | ForEach-Object -Process { $_.Name }
Ради полноты: малоизвестной PSv4 + .ForEach()
метод массива , более comprehensivel обсуждается в этой статье , это еще одна альтернатива :
# By property name (string):
$objects.ForEach('Name')
# By script block (more flexibility; like ForEach-Object)
$objects.ForEach({ $_.Name })
Этот подход аналогичен перечислению членов с теми же компромиссами, за исключением того, что конвейерная логика не применяется; он немного медленнее , но все же заметно быстрее конвейера.
Для извлечения одного значения свойства по имени ( строковый аргумент) это решение соответствует перечислению членов (хотя последнее синтаксически проще).
Скрипт-блок - вариант , позволяет произвольные преобразования ; это более быстрая альтернатива ForEach-Object
cmdlet ( %
), работающая по принципу « все в памяти» .
Сравнение эффективности различных подходов
Вот примерное время для различных подходов, основанное на входной коллекции 10,000
объектов , усредненных по 10 запускам; Абсолютные числа не важны и зависят от многих факторов, но они должны дать вам представление об относительной производительности (тайминги взяты с одноядерной виртуальной машины Windows 10:
Важный
Относительная производительность зависит от того, являются ли входные объекты экземплярами обычных типов .NET (например, в качестве вывода Get-ChildItem
) или [pscustomobject]
экземплярами (например, как выходные данные Convert-FromCsv
).
Причина в том, что [pscustomobject]
свойства динамически управляются PowerShell, и он может получить к ним доступ быстрее, чем обычные свойства (статически определенного) обычного типа .NET. Оба сценария описаны ниже.
Тесты используют коллекции, уже находящиеся в памяти, в качестве входных данных, чтобы сосредоточиться на производительности чистого извлечения свойств. При потоковом вызове командлета / функции в качестве входных данных различия в производительности, как правило, будут гораздо менее выраженными, поскольку время, проведенное внутри этого вызова, может составлять большую часть затраченного времени.
Для краткости %
для ForEach-Object
командлета используется псевдоним .
Общие выводы , применимые как к обычному типу .NET, так и к [pscustomobject]
вводу:
Member-enumeration ( $collection.Name
) и foreach ($obj in $collection)
решения являются самыми быстрыми , в 10 или более раз быстрее, чем самое быстрое решение на основе конвейера.
Удивительно, но % Name
работает намного хуже, чем % { $_.Name }
- см. Эту проблему на GitHub .
PowerShell Core здесь неизменно превосходит Windows Powershell.
Тайминги с обычными типами .NET :
- PowerShell Core v7.0.0-превью.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 версии 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
Выводы:
- В PowerShell Ядра ,
.ForEach('Name')
явно превосходит .ForEach({ $_.Name })
. Любопытно, что в Windows PowerShell последняя работает быстрее, хотя и незначительно.
Тайминги с [pscustomobject]
экземплярами :
- PowerShell Core v7.0.0-превью.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 версии 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
Выводы:
Обратите внимание , как при [pscustomobject]
входе .ForEach('Name')
на сегодняшний день превосходит скрипт-блок на основе варианта, .ForEach({ $_.Name })
.
Точно так же [pscustomobject]
ввод делает конвейер Select-Object -ExpandProperty Name
быстрее, в Windows PowerShell практически на одном уровне .ForEach({ $_.Name })
, но в PowerShell Core по-прежнему примерно на 50% медленнее.
Вкратце: за странным исключением % Name
, [pscustomobject]
строковые методы ссылки на свойства превосходят методы на основе сценариев.
Исходный код для тестов :
Примечание:
Загрузите функцию Time-Command
из этого Gist, чтобы запустить эти тесты.
Вместо этого установите $useCustomObjectInput
для $true
измерения с [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 })
. Иногда это может быть удобнее вводить в командной строке, хотя я думаю, что ответ Скотта в целом лучше.