Поиск по сайту:

Как создать функцию PowerShell


Когда вам нужно многократно использовать часть ваших сценариев или запустить один и тот же код с только различающимися значениями, тогда вам следует создать функцию PowerShell.

Функции PowerShell не только позволяют легко повторно использовать часть вашего скрипта, но также придают структуру вашему скрипту. Это сделает ваши скрипты более читабельными и облегчит отладку, когда что-то не работает так, как должно.

В этой статье мы рассмотрим, как создать функцию PowerShell, используя параметры, возвращая значения и, конечно, как их использовать.

Создание функции PowerShell

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

То, как вы назовете свою функцию, на самом деле очень важно. В PowerShell вам нужно использовать один из одобренных глаголов, за которым следует существительное в единственном числе. Оба пишутся в Pascal case, где первая буква каждого слова пишется с заглавной. Например, Add-UsersToGroup.

Чтобы увидеть список всех утвержденных глаголов, вы можете выполнить командлет Get-Verb в PowerShell или ознакомиться с документацией здесь на сайте Microsoft.

Итак, например, чтобы создать простую функцию, которая будет что-то выводить в консоль (хост), мы можем сделать следующую функцию:

function Write-SomethingToHost {
    Write-Host 'show something on screen'
}

Использование параметров

Параметры позволяют передавать переменные или объекты в вашу функцию PowerShell, которые затем можно использовать внутри функции. Если вы создаете небольшую функцию, вы можете объявить параметр, добавив переменную в скобках после имени функции.

Таким образом, даже возможно объявить несколько параметров, разделяя их запятой. Но этот метод не рекомендуется использовать:

function Show-Something($test)
{
    Write-Host "show something on screen $test"
}

Show-Something -test "test"

Лучший способ использования параметров в вашей функции — это использование блока параметров. Блоки параметров не только легче читать, но и дают вам больше контроля над параметрами.

В приведенном ниже примере мы определили две переменные внутри блока параметров, которые мы хотим использовать в нашей функции:

function Show-Something{

    param(
        $number,
        $color
    )
    Write-Host "show number $number on the screen" -ForegroundColor $color
}

Show-Something -number 42 -color "green"

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

Мы можем предотвратить это, проверяя ввод параметра. Первый шаг — ввести данные. Это означает, что мы собираемся указать, какой тип данных мы ожидаем для параметра. Некоторые из распространенных типов данных, которые используются, это:

  • [строка] – простая строка в одинарных или двойных кавычках
  • [строки[]] – Массив строк (для приема нескольких значений)
  • [int] – Целые числа без десятичных
  • [float] – Числа с плавающей запятой с десятичными знаками
  • [логическое] – Истина или Ложь
  • [массив] – Коллекция значений
  • [хэш-таблица] - коллекция пар «ключ-значение»
  • [дата-время] – значение даты-времени
  • [объект] – .NET Объект

Таким образом, для нашего примера выше мы ожидаем, что обе переменные будут строками:

function Show-Something{

    param(
        [int]$number,
        [string]$color
    )
    Write-Host "show number $number on the screen" -ForegroundColor $color
}

Show-Something -number 42 -color "green"

Валидация параметров

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

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

[ValidateSet("Green", "Red", "Yellow", "Blue")]
[string]$color

Но другие распространенные варианты проверки параметров это:

  • [ValidateRange(1, 100)] – Проверяет диапазон чисел
  • [ValidateLength(1, 10)] – Проверяет длину строкового параметра
  • [ПроверитьШаблон(“[A-Za-z]+”)] – Проверить значения на соответствие регулярному выражению
  • [ValidateNotNullOrEmpty()] – Убедитесь, что параметр не равен null и не пустой
  • [ValidateScript({ $_ -gt 0 })] – Проверка параметра с использованием пользовательского скриптового блока

Итак, для нашей примерной функции мы можем использовать следующие две проверки:

function Show-Something{

    param(
        [ValidateRange(1, 100)]
        [int]$number,
        [ValidateSet("Green", "Red", "Yellow", "Blue")]
        [string]$color
    )
    Write-Host "show number $number on the screen" -ForegroundColor $color
}

Show-Something -number 42 -color "green"

Обязательные параметры

Еще одной распространенной проблемой с параметрами в функциях PowerShell являются пустые значения. Иногда вашему скрипту действительно нужно значение, чтобы выполнить скрипт. В таких случаях вы можете сделать параметр обязательным, добавив [Parameter(Mandatory=$true)].

Например, приведенная ниже функция не сработает, если вы не укажете число. Чтобы этого избежать, мы можем сделать параметр обязательным:

function Get-Square {
    param(
        [Parameter(Mandatory=$true)] 
        [int]$number
    )

    $Number * $Number
}

Если вы хотите определить значение по умолчанию для параметра, то вы не можете сделать его обязательным. Вместо этого вы можете добавить атрибут валидации [ValidateNotNullOrEmpty()], но, на мой взгляд, это довольно бессмысленно, потому что параметр всегда будет возвращаться к значению по умолчанию.

Вернуть результаты

В PowerShell ключевое слово return используется для выхода из функции и возврата значения вызывающему функции. Однако использование return в функциях PowerShell является необязательным. Если вы не используете ключевое слово return, то значение последнего выражения возвращается функцией.

В приведенном ниже примере не нужно использовать ключевое слово return. Результат последнего выражения $InputString.ToUpper() автоматически возвращается функцией:

function ConvertTo-Uppercase {
    param (
        [Parameter(Mandatory=$true)]
        [string]$InputString
    )

    $InputString.ToUpper()
}

ConvertTo-Uppercase -InputString "hello world"

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

Когда в функции встречается return, функция немедленно завершается. Остальной код в функции не будет выполнен. Это может быть полезно, когда выполняется определенное условие, например:

function Test-Return {
    param (
        [Parameter(Mandatory=$true)]
        [int]$number
    )
    if ($number -gt 5) {
        Write-Output "Number is greater than 5"
        return "Exiting function early"
    }
    # This code will not be executed if $number is greater then 5
    Write-Output "This line will not be printed"
}

Test-Return -number 42

Вызвать функцию

Чтобы вызвать функцию в PowerShell, необходимо помнить, что вы сначала должны объявить функцию, прежде чем сможете её вызвать. Поэтому ваши функции находятся в начале скрипта, а вы вызываете их ниже.

При вызове функции вам нужно только ввести имя функции. Если у функции есть параметры, просто введите имя параметра, за которым следует переменная или значение, которое вы хотите ей присвоить.

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

function Show-Something{

    param(
        [ValidateScript({ $_ -gt 0 })]
        [int]$number,
        [ValidateSet("Green", "Red", "Yellow", "Blue")]
        [string]$color
    )
    Write-Host "show number $number on the screen" -ForegroundColor $color
}

This will work, but is not recommended
Show-Something 42 "blue"

Use the parameter names instead
Show-Something -number 42 -color "Blue"

Расширенная функция PowerShell

До сих пор мы рассматривали, как создать базовую функцию PowerShell. Кроме базовой функции, у нас также есть расширенная функция в PowerShell. Принцип расширенной функции остается тем же, но разница в том, что к расширенной функции будут автоматически присвоены некоторые общие параметры, такие как Verbose, Debug, ErrorAction и другие.

Чтобы превратить функцию в расширенную функцию, вам нужно просто добавить [CmdletBinding()] к функции.

function ConvertTo-Uppercase {
    [CmdletBinding()]  # < This makes it an advanced function
    param (
        [Parameter(Mandatory=$true)]
        [string]$InputString
    )

    $InputString.ToUpper()
}

Хорошо знать, что когда вы превращаете функцию в расширенную функцию, вам также нужно будет включить блок параметров, даже если у вас нет никаких параметров.

С включенными расширенными функциями вы теперь можете, например, использовать такие возможности, как Write-Verbose в вашей функции.

function ConvertTo-Uppercase {
    [CmdletBinding()]  # < This makes it an advanced function
    param (
        [Parameter(Mandatory=$true)]
        [string]$InputString
    )
    Write-Verbose "Converting string $InputString to uppercase"
    $InputString.ToUpper()
}

Теперь мы можем вызвать функцию с параметром -Verbose или без него, чтобы показать, что происходит в функции:

Ввод данных в трубопровод

Чтобы использовать вашу функцию в конвейере, вам необходимо указать, какие параметры принимают значения из конвейера. Для этого есть два варианта: вы можете использовать ValueFromPipeline, который принимает значение в зависимости от типа данных, или использовать ValueFromPipelineByPropertyName, который позволяет вам сопоставить имя параметра.

Кроме того, когда вы хотите использовать свою функцию в конвейере, вам нужно будет добавить блок process хотя бы в вашу функцию.

Итак, когда вы используете ValueFromPipeLine, вы можете передать только одну строку через конвейер в вашу функцию. Рассмотрим следующую функцию в качестве примера:

function ConvertTo-StyledProcesses {
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline=$true)]
        [string]$Name
    )

    process {
        Write-Host "Process name is $($Name.ToUpper())"
    }
}

Если мы передадим нашу функцию после cmdlet Get-Service и выберем только свойство Name, то мы сможем преобразовать имя процесса в верхний регистр:

Но что если мы хотим также включить статус в результаты? Предположим, мы хотим задать цвет статуса в зависимости от того, работает ли служба или нет. Если вы посмотрите на функцию ниже, вы можете подумать, что это сработает, но проблема в том, что мы не определили, как атрибуты службы должны быть сопоставлены с параметрами.

function ConvertTo-StyledProcesses {
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline=$true)]
        [string]$Name,

        [Parameter(ValueFromPipeline=$true)]
        [string]$Status
    )

    process {
        $color = "Green"

        if ($Status -eq "Stopped") {
            $color = "Red"
        }

        Write-Host "Process $Name is " -NoNewline
        Write-Host $Status -ForegroundColor $color
    }
}

Таким образом, если мы запустим это так, то атрибуты имени и статуса будут разбиты на параметры имени и статуса:

Чтобы решить эту проблему, нам потребуется использовать опцию ValueFromPipelineByPropertyName вместо этого. Таким образом, мы сможем автоматически сопоставить свойства с правильными параметрами:

function ConvertTo-StyledProcesses {
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipelineByPropertyName=$true)]
        [string]$Name,

        [Parameter(ValueFromPipelineByPropertyName=$true)]
        [string]$Status
    )

    process {
        $color = "Green"

        if ($Status -eq "Stopped") {
            $color = "Red"
        }

        Write-Host "Process $Name is " -NoNewline
        Write-Host $Status -ForegroundColor $color
    }
}

Начало, Процесс, Конец

Кроме блока процесса внутри функции, мы также можем иметь блоки begin и end. Блоки begin и end выполняются только один раз при вызове функции. Они являются необязательными и не используются очень часто, но в некоторых случаях могут быть полезны.

Например, функция ниже будет вычислять общую цену товаров на основе их количества и цен на товары. Она также будет вычислять общую цену всех товаров вместе:

function Get-TotalPrice {
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline=$true)]
        [ValidateNotNull()]
        [pscustomobject]$Item
    )

    begin {
        $totalPrice = 0
    }

    process {
        $quantity = $Item.Quantity
        $unitPrice = $Item.UnitPrice

        $totalItemPrice = $quantity * $unitPrice
        $totalPrice += $totalItemPrice

        # Outputting each item's total price
        [PSCustomObject]@{
            Item = $Item.Name
            TotalPrice = $totalItemPrice
        }
    }

    end {
        Write-Output "Overall Total Price: $totalPrice"
    }
}

Вы можете запустить/попробовать скрипт выше с помощью следующего примера:

Define items
$item1 = [pscustomobject]@{
    Name = "Product A"
    Quantity = 3
    UnitPrice = 10
}

$item2 = [pscustomobject]@{
    Name = "Product B"
    Quantity = 2
    UnitPrice = 15
}

Calculate total price
$item1, $item2 | Get-TotalPrice

Result
Item      TotalPrice
----      ----------
Product A         30
Product B         30
Overall Total Price: 60

Заключение

Функции PowerShell - отличный способ повторного использования фрагментов кода в ваших скриптах и придания им структуры. Старайтесь делать ваши функции небольшими и пусть они выполняют только одну задачу.

При наименовании ваших функций убедитесь, что вы используете только одобренные глаголы, чтобы ваши функции было легко понять другим. Также убедитесь, что вы используете уникальные имена.

Надеюсь, вы нашли эту статью полезной. Если у вас есть вопросы, просто оставьте комментарий ниже.