Contents of this directory is archived and no longer updated.

Примечание: сведения в данной статье могут содержать фрагменты диктовки Капитана Очевидности и скорее всего приведены только для освежения памяти.


Часть 1 — $_

Давным-давно, Вася Гусев как-то сотворил артикль: Непонятные штуки – $_ и %. Как показала практика (хотя, я с этой практикой не сильно согласен), эти штуки до сих пор (даже после Васиного срыва покровов) остаются непонятными для рядовых администраторов у которых нет интегрированного парсера и отладчика скриптов в голове. Если  со знаком процента ещё можно бороться (т.е. не использовать его в качестве алиаса Foreach-Object), то с $_ бороться тяжело. Тут даже дело не в том, что бороться, а просто объяснить пользователям, что это такое. Что это такое — можно почитать у Васи. Если вы ленивые, тогда объясняю: $_ представляет собой динамическую переменную, которая хранит значение текущего элемента в конвейере.

Пара слов о понимании. Например, переменная $files для пользователя выглядит достаточно понятной — в ней какой-то список файлов. $_ для пользователя уже непонятна — в ней хранится нечто. В PowerShell 2.0 появилась возможность избежать использования этой мутной перменной в advanced functions. Вот краткая история:

  • PowerShell 1.0
function EnumItems {    process {
        Write-Host Current pipeline item: $_
    }
}

И запустите: 1..5 | EnumItems 0

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

  • PowerShell 2.0

В PowerShell 2.0 предыдущий код будет работать, пока вы не превратите простую функцию в advanced:

function EnumItems {
[CmdletBinding()]
    param($a)
    process {
        Write-Host Current pipeline item: $_    }
}

В таком виде функция не заработает уже. Дело в том, что в advanced functions вы обязаны использовать конструкцию param() для определения аргументов, которые эта функция принимает. Причём, если функция не принимает никаких внешних аргументов, а принимает только данные из конвейера вы всё равно должны это отразить в конструкции param().

function EnumItems {
[CmdletBinding()]
    param([Parameter(ValueFromPipeline = $true)]$a)
    process {
        Write-Host Current pipeline item: $a
    }
}

Теперь можно запускать функцию: 1..5 | EnumItems

Зато в стадии Process (которая запускается для каждого элемента в конвейере) вы теперь можете совершенно невозбранно использовать не $_, а нормальное имя переменной, которая принимает значения из конвейера. Это делает функцию более читабельной. А что с остальными? Try {} Catch {}, trap {}, Foreach-Object {}? В этих конструкциях для обозначения текущего элемента конвейера используется закорючка $_.

  • PowerShell 3.0

Для улучшения (или упрощения) понимания сути переменной $_ в PowerShell 3.0 сделали алиас для неё — $psitem. $psitem можно использовать абсолютно везде, где вы можете использовать $_:

1..5 | ForEach-Object {$psitem}
function DivideByZero {
    try {1/0}
    catch {Write-Host $psitem}
}

Да, в catch {} можно использовать переменную $_ или $psitem, которая представляет собой текущий элемент ошибки — $error[0]. Или ещё вот:

switch (1..5) {
    1 {$psitem}
}

В конструкции switch тоже можно использовать $_, или $switch.current, или $psitem , которые представляют одно и то же — текущий элемент энумератора.

Часть 2 — атрибуты переменных

Вернёмся к истории. Во времена PowerShell 1.0 мы писали какие-то интерактивные функции, которые требовали ввода каких-то данных пользователем и внутренняя логика функции что-то с ними делала. Но пользователи (включая администраторов и разработчиков этой функции) — существа доволно-таки, неблагодарные и всегда наровят что-нибудь поделить на ноль и, в конечном итоге, всё сломать. Всё что угодно, лишь бы не работать. Для этого в первых функциях приходилось писать кучу бессмысленного кода, который выполнял одну задачу — проверить, что пользователь ввёл те данные, которые функция ожидает получить. Например, пользователь должен указать хоть какие-то аргументы, а не просто выполнить функцию без указания каких-либо данных. Например, если параметр работает с числами, функция должна быть защищена от пользователя-вредителя, который обязательно в параметр укажет строку, а не число и многое другое. В PowerShell 1.0 создание защиты от дураков и прочих вредителей было не самым простым делом.

В PowerShell 2.0 коварные разработчики PowerShell (с Джефри Сновером во главе отряда) придумали advanced functions. Это позволило писателям функций резко повысить свою производительность за счёт более продвинутых средств борьбы с анонимусомвредителями. Для этого в параметрах функции можно указывать более конкретные фильтры для принимаемых данных. Вот пример:

function Foo {
[CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [ValidateCount(1,7)]
        [ValidatePattern("Bad|Locked|Missed|New|Ok|Total|Unknown")]
        [String[]]$show
    )
}

Если оградить параметр вот такими атрибутами, пользователь будет вынужден:

  1. Обязательно указать значение параметра;
  2. В значения параметра указывать только что-то, что указано в ValidatePattern;
  3. Значений параметра может быть от 1 до 7.

Вот что мы получим, если не будем соблюдать эти требования:

[↓] [vPodans] function Foo {
>> [CmdletBinding()]
>>     param(
>>         [Parameter(Mandatory = $true)]
>>         [ValidateCount(1,7)]
>>         [ValidatePattern("Bad|Locked|Missed|New|Ok|Total|Unknown")]
>>         [String[]]$show
>>     )
>> }
>>
[↓] [vPodans] foo

cmdlet Foo at command pipeline position 1
Supply values for the following parameters:
show[0]:
Foo : Cannot validate argument on parameter 'show'. The number of supplied arguments (0) is less than the minimum numbe
r of allowed arguments (1). Specify more than 1 arguments and then try the command again.
At line:1 char:4
+ foo <<<<
    + CategoryInfo          : InvalidData: (:) [Foo], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Foo

[↓] [vPodans] foo nya!
Foo : Cannot validate argument on parameter 'show'. The argument "nya!" does not match the "Bad|Locked|Missed|New|Ok|To
tal|Unknown" pattern. Supply an argument that matches "Bad|Locked|Missed|New|Ok|Total|Unknown" and try the command agai
n.
At line:1 char:4
+ foo <<<<  nya!
    + CategoryInfo          : InvalidData: (:) [Foo], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Foo

[↓] [vPodans] foo bad, bad, bad, bad, bad, bad, bad, bad
Foo : Cannot validate argument on parameter 'show'. The number of supplied arguments (8) exceeds the maximum number of
allowed arguments (7). Specify less than 7 arguments and then try the command again.
At line:1 char:4
+ foo <<<<  bad, bad, bad, bad, bad, bad, bad, bad
    + CategoryInfo          : InvalidData: (:) [Foo], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Foo

[↓] [vPodans]

На любую попытку обмануть функцию, она ответит вам кучей красного текста (видимо, намекает на кровавую расправу). Теперь, в PowerShell 3.0, эти атрибуты можно использовать не только в секции param() функций, но для любых переменных в консоли:

[↓] [vPodans] [ValidateRange(1,10)][int]$x = 1
[↓] [vPodans] $x = 11
The variable cannot be validated because the value 11 is not a valid value for the x variable.
At line:1 char:1
+ $x = 11
+ ~~~~~~~
    + CategoryInfo          : MetadataError: (:) [], ValidationMetadataException
    + FullyQualifiedErrorId : ValidateSetFailure

[↓] [vPodans]

И последнее про атрибуты. Для булевых атрибутов теперь не обязательно указывать $true, например:

function Foo {
[CmdletBinding()]
    param(
        [Parameter(Mandatory, ValueFromPipeline)]
        [int]$a
    )
    process {$a}
}

На сегодня это всё.


Share this article:

Comments:

Comments are closed.