Разница между ForEach и ForEach-Object в powershell

Есть ли разница между ForEach и ForEach-Object?

У меня такой небольшой код, он отлично работает

$txt = Get-Content 'C:\temp\000.txt'
$result = foreach ($line in $txt) {$line.replace(".ini","")}
$result | out-file 'c:\temp\001.txt'

Но если я использую "ForEach-Object", у меня появились ошибки....

$txt = Get-Content 'C:\temp\000.txt'
$result = foreach-object ($line in $txt) {$line.replace(".ini","")}
$result | out-file 'c:\temp\001.txt'

Почему? и как вывести результаты цикла с помощью ForEach-Object

Ответ 1

foreach является псевдонимом ForEach-Object, но он также является ключевым словом (что запутывает).

Синтаксис foreach ($<item> in $<collection\>){<statement list>}, который вы используете, это help about_foreach.

foreach как ForEach-Object псевдоним help ForEach-Object.

Ключевое слово foreach работает над каждым $<item> в $<collection>, как указано в бит ().

Псевдоним foreach/function ForEach-Object работает над каждым элементом коллекции, который он получает в качестве входного файла.

Ответ 2

Это разные команды для разных целей. В конвейере используется командлет ForEach-Object , и вы используете $PSItem или $_ для ссылки на текущий объект, чтобы запустить {scriptblock} следующим образом:

1..5 | ForEach-Object {$_}

>1
>2
>3
>4
>5

Теперь вы также можете использовать очень похожее ключевое ключевое слово, ForEach, в начале строки. В этом случае вы можете запустить {scriptblock}, в котором вы определяете имя переменной, например:

ForEach ($number in 1..5){$number}
>1
>2
>3
>4
>5

Основное различие здесь заключается в том, где вы используете эту команду: одна используется в середине конвейера, а другая запускает собственный конвейер. В сценариях стиля работы я бы рекомендовал вместо ключевого слова использовать ключевое слово ForEach.

Ответ 3

Оба предыдущих ответа верны, но https://blogs.technet.microsoft.com/heyscriptingguy/2014/07/08/getting-to-know-foreach-and-foreach-object/ имеет хорошее резюме:

Когда вы вводите данные в ForEach, это псевдоним ForEach-Object. Но когда вы помещаете ForEach в начале строки, это оператор Windows PowerShell.

и больше деталей:

Оператор ForEach загружает все элементы заранее в коллекцию, а затем обрабатывает их по одному. ForEach-Object ожидает, что элементы будут передаваться по конвейеру, что снижает требования к памяти, но в то же время снижает производительность.

Затем он включает некоторые измерения производительности и делает вывод:

Так какой из них вы используете? Ну, ответ: "Это зависит". Вы можете перебирать коллекцию элементов с помощью оператора ForEach или командлета ForEach-Object. ForEach идеально подходит, если у вас достаточно памяти, вы хотите добиться максимальной производительности и не заботитесь о передаче вывода другой команде через конвейер. ForEach-Object (с его псевдонимами% и ForEach) принимает данные из конвейера. Хотя процесс обработки выполняется медленнее, он дает вам преимущества блоков Begin, Process и End. Кроме того, он позволяет передавать объекты в другую команду через конвейер. В конце концов, используйте подход, который наилучшим образом соответствует вашим требованиям и возможностям вашей системы.

Ответ 4

Помимо технических отличий, которые были упомянуты ранее, вот некоторые практические отличия для полноты картины (см. Также):

1.) В конвейере вы не знаете общее количество обработанных предметов. Это ситуация, когда вы решили сначала получить полный список, а затем выполнить цикл foreach.

Пример:

$files = gci "c:\fakepath"
$i = 0
foreach ($file in $files) {
    $i++
    Write-Host "$i / $($files.Count) processed"
}

2.) При существующем списке цикл foreach работает быстрее, чем версия конвейера, поскольку блок сценария не нужно вызывать каждый раз. (Но разница может быть незначительной в зависимости от выполняемой работы и количества предметов.)

Пример:

$items = 0..100000
Measure-Command { $items | ForEach-Object { $_ } }
# ~500ms on my machine
Measure-Command { foreach ($i in $items) { $i } }
# ~70ms on my machine