Pull to refresh

Advanced PowerShell vol. 1: повторное использование кода

Reading time 6 min
Views 34K
Привет! Как большой поклонник и активный практик PowerShell я часто сталкиваюсь с тем, что мне необходимо повторно использовать ранее написанные куски кода.
Собственно, для современных языков программирования code reuse — это обычное дело.
PowerShell в этом вопросе не отстает, и предлагает разработчикам (написателям скриптов) сразу несколько механизмов обращения к написанному ранее коду.
Вот они по возрастанию сложности: использование функций, дот-сорсинг и написание собственных модулей.

Рассмотрим их все порядку.

В качестве решения лабораторной задачи напишем скрипт, который расширяет раздел C:\ до максимально возможного размера на удаленном Windows-сервере LAB-FS1.
Такой скрипт будет состоять из одной строки и выглядеть так:

Invoke-Command -ComputerName LAB-FS1 -ScriptBlock { "rescan", "select volume=c", "extend" | diskpart }

Работает это так. Сначала PowerShell устанавливает удаленное соединение с сервером LAB-FS1 и запускает на нем локально набор команд, заключенный в фигурные скобки параметра -ScriptBlock. Этот набор в свою очередь последовательно передает команде diskpart три текстовых параметра, а diskpart выполняет (по очереди) повторное сканирование разделов, выбор раздела C:\ и расширение его до максимально возможного размера.



Как видите, скрипт крайне простой, но в то же время крайне полезный.
Рассмотрим, как правильно упаковать его для повторного использования.

1. Использование функций


Самый простой вариант.

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

function ExtendDisk-Remotely
{
    param (
     [Parameter (Mandatory = $true)]
        [string] $ComputerName,
     [Parameter (Mandatory = $false)]
        [string] $DiskDrive = "c"
    )

    Invoke-Command -ComputerName $ComputerName -ScriptBlock {"rescan", "select volume=$using:DiskDrive", "extend" | diskpart}
}

ExtendDisk-Remotely -ComputerName LAB-FS1

Здесь для функции ExtendDisk-Remotely заданы два параметра:

  • Обязательный ComputerName;
  • Необязательный DiskDrive. Есть не задать имя диска явным образом, скрипт будет работать с диском C:\

Также можно отметить, что передача локальной переменной в удаленную сессию осуществляется с помощью ключевого слова using.

Сохраним скрипт под именем Example-01-Functions.ps1 и запустим:



Видим, что наша функция успешно вызвалась и расширила раздел C:\ на сервере LAB-FS1.

2. Дот-сорсинг


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

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

Создадим отдельный файл для всех наших функций и назовем его Example-02-DotSourcing.ps1.
Его содержимое будет таким:

function ExtendDisk-Remotely
{
    param (
     [Parameter (Mandatory = $true)]
        [string] $ComputerName,
     [Parameter (Mandatory = $false)]
        [string] $DiskDrive = "c"
    )

    Invoke-Command -ComputerName $ComputerName -ScriptBlock {"rescan", "select volume=$using:DiskDrive", "extend" | diskpart}
}

Это объявление функции (без вызова), которая теперь хранится у нас в отдельном файле и может быть вызвана в любой момент с помощью техники, которая называется dot-sourcing. Синтаксис выглядит так:

. C:\Scripts\Example-02-DotSourcing.ps1
ExtendDisk-Remotely LAB-FS1

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

Такой синтаксис позволяет нам подключить к текущему скрипту содержимое файла Example-02-DotSourcing.ps1. Это то же самое, что использовать директиву #include в C++ или команду using в C# — подключение кусков кода из внешних источников.
После подключения внешнего файла мы уже во второй строке можем вызвать входящие в него функции, что мы успешно и делаем. При этом задотсорсить внешний файл можно не только в теле скрипта, но и в «голой» консоли PowerShell:



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

3. Написание собственного модуля PowerShell


Внимание: Я использую в работе PowerShell версии 4.
Одна из его особенностей заключается в том, что он автоматически подгружает в оперативную память модули по мере обращения к ним, без использования командлета Import-Module.

В старых версиях PowerShell (начиная с 2) написанное ниже будет работать, но может потребовать дополнительных манипуляций, связанных с предварительным импортом модулей перед их использованием.
Мы же будем рассматривать современные среды.

Усложняем ситуацию еще раз.

Мы стали очень хорошими PowerShell-программистами, написали сотни полезных функций, для удобства использования разделили их на десятки .ps1-файлов, и в нужные нам скрипты дотсорсим нужные файлы. А если у нас десятки файлов для дотсорсинга и их надо указать в сотне скриптов? А если мы несколько из них переименовали? Очевидно, что придется менять путь ко всем файлам во всех скриптах — это страшно неудобно.

Поэтому.

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

Что такое модуль Windows PowerShell


Модуль Windows PowerShell — это набор функционала, который в том или ином виде размещен в отдельных файлах операционной системы. Например, все родные Микрософтовские модули являются бинарными и представляют собой скомпилированные .dll. Мы же будем писать модуль скриптовый — скопируем в него код из файла Example-02-DotSourcing.ps1 и сохраним как файл с расширением .psm1.

Чтобы понять, куда сохранять, посмотрим содержимое переменной окружения PSModulePath.



Видим, что по умолчанию у нас есть три папки, в которых PowerShell будет искать модули.
Значение переменной PSModulePath можно редактировать с помощью групповых политик, задавая таким образом пути к модулям для всей сети, но это другая история, и рассматривать ее сейчас мы не будем. Зато будем работать с папкой C:\Users\Administrator\Documents\WindowsPowerShell\Modules и сохраним наш модуль в нее.
Код остается неизменным:

function ExtendDisk-Remotely
{
    param (
     [Parameter (Mandatory = $true)]
        [string] $ComputerName,
     [Parameter (Mandatory = $false)]
        [string] $DiskDrive = "c"
    )

    Invoke-Command -ComputerName $ComputerName -ScriptBlock {"rescan", "select volume=$using:DiskDrive", "extend" | diskpart}
}

Меняется лишь папка, в которую сохраняется файл, и его расширение.
Очень важно!
Внутри папки Modules необходимо создать подпапку с именем нашего модуля. Пусть этим именем будет RemoteDiskManagement. Сохраняем наш файл внутрь этой подпапки и даем ему точно такое же имя и расширение .psm1 — получаем файл C:\Users\Administrator\Documents\WindowsPowerShell\Modules\RemoteDiskManagement\RemoteDiskManagement.psm1.

Наш модуль готов, и мы можем проверить, что он виден в системе:



Модуль виден, и в дальнейшем мы можем вызывать его функции без предварительного объявления их в теле скрипта или дотсорсинга.
PowerShell будет воспринимать нашу функцию ExtendDisk-Remotely как «встроенную» и подключать ее по мере необходимости:



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

4. Другие advanced-возможности


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

Интересно?
Тогда попробую выдавать по одной статье в неделю.
Tags:
Hubs:
+6
Comments 12
Comments Comments 12

Articles