ForEach-Object и его скриптблоки

Командлет ForEach-Object предназначен для выполнения указанных нами действий с каждым из элементов массива.

Этот массив может быть указан в качестве значения параметра -InputObject, однако в подавляющем большинстве случаев элементы передаются командлету ему по конвейеру.

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

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

Для обращения к обрабатываемому в данный момент объекту мы используем автоматические переменные $_ или $PSItem, являющиеся полностью равнозначными.

Process

В самом простом случае мы можем использовать командлет ForEach-Object следующим образом.

Get-Service | ForEach-Object -Process { if ($_.StartType -eq 'Automatic' -and $_.Status -eq 'Stopped') { Start-Service $_ } }

Или же мы можем воспользоваться переменной.

$ScriptBlock = { if ($_.StartType -eq 'Automatic' -and $_.Status -eq 'Stopped') { Start-Service $_ } }
​
Get-Service | ForEach-Object -Process $ScriptBlock

Здесь в качестве значения параметра -Process мы указали скриптблок, который будет выполняться для каждого поступающего по конвейеру объекта службы.

Alias

Командлет ForEach-Object обладает двумя алиасами — foreach и %. То есть следующие команды будут полностью равнозначными.

Get-Service | ForEach-Object Name
Get-Service | foreach Name
Get-Service | % Name

Однако же вернемся к скриптблокам.

Begin и End

Кроме параметра -Process, мы можем задать параметры -Begin и -End. Каждый из них выполняется один раз, вне зависимости от количества поступающих элементов: -Begin — перед началом их обработки, а -End — после того, как все элементы уже были обработаны.

Оба эти параметра в качестве значения принимает скриптблок, однако их отличие от параметра -Process в том, что, во-первых, они принимают единственный скриптблок, когда как -Process может принимать массив скриптблоков, а во-вторых, скриптблоки, указанные в этих параметрах не могут обращаться к поступающим объектам при помощи автоматических переменных $_ или $PSItem.

В целом, параметр -Begin используется для подготовки среды к обработке элементов, а -End — для неких завершающих действий.

Get-Process | ForEach-Object -Begin { [long]$WorkingSet = 0 } -Process { $WorkingSet += $_.WS } -End { $WorkingSet }

Кроме параметров -Begin, -Process и -End, существует еще один параметр: -RemainingScripts. Он, как и -Process, принимает массив скриптблоков и, в сущности, при выполнении командлета ForEach-Object они добавляются к массиву скриптбоков параметра -Process. Для чего он нужен мы поговорим ниже.

Positional parameters

Благодаря тому, что PowerShell поддерживает использование позиционных параметров, во многих командлетах мы можем пропустить имена некоторых параметров и указать только их значения.

То есть, мы можем указать командлет ForEach-Object следующим образом.

"Value" | ForEach-Object {"1: $_"} {"2: $_"} {"3: $_"}

Если мы запустим командлет Trace-Command для компонента ParameterBinding

Trace-Command -Name ParameterBinding -Expression { "Value" | ForEach-Object {"1: $_"} {"2: $_"} {"3: $_"} } -PSHost

то увидим, что первый аргумент — {«1: $_»} — был назначен параметру -Process, а все остальные — уже упомянутому нами параметру -RemainingScripts.

Происходит так потому, что в коде в определении параметра -RemainingScripts указан атрибут ValueFromRemainingArguments со значением true, и это указывает, что все не назначенные другим параметрам аргументы должны быть сопоставлены ему.

Если же мы запустим изначальную команду на выполнение то получим следующий результат.

1:
2: Value
3:

Почему так получилось?

Как мы уже говорили выше, в коде командлета ForEach-Object значения параметра -RemainingScripts добавляются в общий массив скриптблоков, после тех, что были указаны в параметре -Process. Далее PowerShell смотрит, указаны ли явным образом параметры -Begin и -End. Если нет, то первый скриптблок из массива становится начальным скриптблоком, как если бы он был указан в параметре -Begin, а последний скриптблок становится завершающим, таким, как можно было бы указать в параметре -End.

И если мы передадим командлету массив из двух элементов, мы увидим это более явно.

"Value1", "Value2" | ForEach-Object {"1: $_"} {"2: $_"} {"3: $_"}
1:
2: Value1
2: Value2
3:

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

"Value1", "Value2" | ForEach-Object {"1: $_"} {"2: $_"}
1:
2: Value1
2: Value2

Если же скриптблоков больше двух, то, как мы видели, первый и последний из них становятся начальным и завершающим, соответственно.

"Value1", "Value2" | ForEach-Object {"1: $_"} {"2: $_"} {"3: $_"} {"4: $_"}
1:
2: Value1
3: Value1
2: Value2
3: Value2
4:

И снова Begin и End

Для того, чтобы все указанные нами скриптблоки выполнялись для поступающих элементов, мы можем явным образом указать значения параметров -Begin и -End. Например, так.

"Value" | ForEach-Object {"1: $_"} {"2: $_"} {"3: $_"} -Begin {} -End {}

Или же так.

"Value" | ForEach-Object {"1: $_"} {"2: $_"} {"3: $_"} -Begin $null -End $null

В обоих случаях результат будет следующим.

1: Value
2: Value
3: Value

Кроме того, мы можем добавить пустые скриптблоки в качестве первого и последнего аргумента.

"Value" | ForEach-Object {} {"1: $_"} {"2: $_"} {"3: $_"} {}

Принимая во внимание все вышесказанное, мы можем представить использованную нами в начале статьи команду следующим образом.

Get-Process | ForEach-Object { [long]$WorkingSet = 0 } { $WorkingSet += $_.WS } { $WorkingSet }

Return

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

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход /  Изменить )

Google photo

Для комментария используется ваша учётная запись Google. Выход /  Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход /  Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход /  Изменить )

Connecting to %s