Давайте рассмотрим, как создавать функции, определять входные параметры и работать с функциями в сценариях на PowerShell.

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

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

function <имя> {<фрагмент кода>}

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

function FileSize1
{
   dir C:Windows |
   where {$_.length –gt 100000}
}

Обратите внимание, что при вводе нескольких строк на консоли строки нужно разделять, нажимая клавишу Enter. При этом в начале новой строки отображается запрос ввода >>, означающий, что ввод исполняемого предложения не завершен и необходимо продолжить его. Завершив ввод определения функции, нажмите Enter дважды, чтобы вернуться в обычный режим ввода команд (>).

В приведенном примере видно, что определение функции начинается с ключевого слова function, за которым следует имя функции. Фрагмент кода включает две команды в одном конвейере обработки. Первая команда использует команду Get-ChildItem, представленную псевдонимом dir, для получения содержимого папки C:Windows. Результаты запроса передаются по конвейеру второй команде, которая использует команду Where-Object, представленную псевдонимом where, для фильтрации файлов таким образом, чтобы вернуть в результате только файлы объемом более 100 000 байт.

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

FileSize1

При нажатии клавиши Enter PowerShell выполняет код, включенный в тело функции, и возвращает результат, показанный на экране 1. Это тот же результат, который мы получили бы, введя исполняемые предложения из тела функции в командной строке PowerShell.

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

Обработка аргументов вызова

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

Например, следующее определение функции использует $args:

function FileSize2
{
   dir $args [0] |
   where {$_.Length -gt 100000}
}

Обратите внимание, что первая команда в фрагменте сценария обрабатывает первое значение ($args [0]) из массива $args, а не «зашитое» в коде значение (C:Windows). В результате при вызове функции FileSize2 PowerShell использует первый аргумент из введенной строки вызова для определения папки. Если указать более одного аргумента для этой функции, PowerShell проигнорирует избыточные аргументы, как не обрабатываемые данной функцией.

Для вызова функции FileSize2 нужно просто ввести ее имя и через пробел — путь к папке:

FileSize2 C:Windows

Получив эту команду, Windows PowerShell вызывает нашу функцию, подставляет вместо $args [0] значение C:Windows и возвращает полученные данные о файлах в этой папке, так же как на экране 1. Имейте в виду, что если в пути к папке есть пробелы, то весь путь нужно заключить в кавычки.

При вызове функции каждый из указываемых аргументов добавляется в массив $args. В результате мы получаем возможность обработать произвольное количество аргументов нашей функции. Однако такой способ работы с аргументами с ростом их числа приводит к путанице. Особенно неприятно, когда аргументы вводятся не в том порядке, который предусмотрен логикой в теле функции. Кроме того, мы ограничены в возможностях определения аргументов. Поэтому зачастую более эффективным способом работы с аргументами вызова является создание в определении функции именованных параметров.

Имена аргументов вызова функции указываются с префиксом $ в круглых скобках сразу за именем функции. При задании нескольких именованных аргументов они разделяются запятыми. В качестве примера посмотрим на следующее определение функции:

function FileSize3 ($dir, $minSize)
{
   dir $dir |
   where {$_.length -gt $minsize}
}

Здесь определены два именованных параметра: $dir и $minSize. Тело функции использует эти параметры для поиска обрабатываемой папки и порогового значения размера файла в байтах соответственно.

Вызывая функцию FileSize3, мы указываем имя и значение аргумента точно так же, как в случае параметров вызова команды: имя атрибута указывается с префиксом — (минус вместо знака доллара, используемого при определении функции), а за ним через пробел идет передаваемое значение. Остальные пары имен и значений именованных параметров вызова указываются аналогичным образом, и разделителем также служит пробел:

FileSize3-ir C:Windows
   –minSize 100000

Обратите внимание, что в данном случае мне пришлось использовать обратный апостроф (`) для указания второго атрибута на отдельной строке.

При вызове функций с именованными аргументами PowerShell выполняет код функции, заменяя имена параметров в теле функции значениями аргументов вызова. Например, PowerShell заменяет $minSize значением 100000. Затем PowerShell возвращает набор результатов, полученных в ходе работы фрагмента сценария, как показано на экране 2.

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

FileSize3 C:Windows 100000

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

Определение значений параметров по умолчанию

В некоторых случаях желательно использовать некие предопределенные значения каких-либо аргументов вызова, когда соответствующие значения не указаны при вызове функции. Проще всего это достигается определением значений по умолчанию для параметров в определении функции. Например, следующее определение функции включает значения по умолчанию для $dir и $minSize:

function FileSize4
   ($dir=»C:WindowsSystem32»,
      $minSize=1000000)
{
   dir $dir |
   where {$_.Length -gt $minSize}
}

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

FileSize4

Как показано на экране 3, PowerShell при этом автоматически подставляет значения по умолчанию вместо параметров в теле функции.

Вместо значений по умолчанию при необходимости можно использовать любые другие:

FileSize4 C:Windows 500000

Функция возвращает данные, показанные на экране 4, исходя из указанных значений.

Можно также одни аргументы указать при вызове функции, а другие не указывать. Например, следующая команда передает значение первого параметра ($dir), но не определяет значение второго ($minSize):

FileSize4 C:Windows

При выполнении этой команды для $dir будет использоваться значение C:Windows и заданное в определении функции значение для параметра $minSize. В итоге набор результатов будет содержать список файлов размером более 1 000 000 байт в папке C:Windows.

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

FileSize4-minSize 500000

Функция будет использовать предопределенное значение для $dir и значение 500 000 для $minSize, выводя список файлов размером более 500 000 байт, расположенных в папке C:Windowssystem32. Получив только значение 500000 без имени параметра, PowerShell пришлось бы это значение использовать в качестве параметра $dir, поскольку первым аргументом в определении функции указан $dir. Именно поэтому необходимо было указать имя аргумента.

Типизация аргументов вызова

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

function FileSize5
   ([string] $dir="C:Windows",
      [int] $minSize=1000000)
{
   dir $dir |
   where {$_.Length -gt $minSize}
}

Теперь $dir определен как аргумент типа String, а $minSize — как Int32. Если попытаться задать значение неприводимого типа для одного из аргументов, мы получим сообщение об ошибке. Например, следующая команда задает значение типа String для целочисленного аргумента $minSize:

FileSize5-minSize file

Как показано на экране 5, результатом выполнения будет ошибка, поскольку PowerShell не может преобразовать строковое значение «file» к типу Int32.

Работа с функциями

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

$files = FileSize5
C:Windows 500000
foreach ($file in $files)
   {
      $file.Name + " is " +
         $file.Length + " bytes."
}

Этот код использует функцию FileSize5 для получения списка файлов и помещения этого списка в переменную $files. Данная переменная затем используется в качестве коллекции в цикле foreach для вывода имен и размеров файлов, как показано на экране 6.

Экран 6. Использование функции для задания переменной

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

FileSize5 C:Windows 500000 |
foreach {$_.name + " is " +
   $_.length + " bytes."}

Результаты вызова функции при этом передаются команде ForEach-Object, представленной псевдонимом foreach, которая выводит информацию о каждом из файлов, как показано на экране 7.

Экран 7. Использование функции в конвейере обработки

Заглянем вперед

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

Роберт Шелдон (contact@rhsheldon.com) — технический консультант и автор книг по технологиям Microsoft Windows и базам данных


Экран 1. Создание и результат выполнения функции FileSize1

Экран 2. Добавление именованных параметров вызова функции

Экран 3. Определение значений по умолчанию для параметров

Экран 4. Использование аргументов вызова для переопределения значений параметров

Экран 5. Жесткая типизация параметров функции