Поделиться через


Глава 10. Модули скриптов

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

Функции точечного подключения

Одна вещь, которую мы не охватывали в предыдущей главе, — это функции dot-sourcing. Если вы определяете функцию в скрипте, но не в составе модуля, единственным способом его загрузки в память является dot-sourcing его .ps1 файл.

Например, сохраните следующую функцию в файле с именем Get-MrPSVersion.ps1.

function Get-MrPSVersion {
    $PSVersionTable
}

При запуске скрипта кажется, что ничего не происходит.

.\Get-MrPSVersion.ps1

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

Get-MrPSVersion
Get-MrPSVersion : The term 'Get-MrPSVersion' is not recognized as the name
of a cmdlet, function, script file, or operable program. Check the spelling
of the name, or if a path was included, verify that the path is correct and
try again.
At line:1 char:1
+ Get-MrPSVersion
+ ~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (Get-MrPSVersion:String) [],
   CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

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

Get-ChildItem -Path Function:\Get-MrPSVersion
Get-ChildItem : Cannot find path 'Get-MrPSVersion' because it does not
exist.
At line:1 char:1
+ Get-ChildItem -Path Function:\Get-MrPSVersion
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (Get-MrPSVersion:String) [Get
   -ChildItem], ItemNotFoundException
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.Ge
   tChildItemCommand

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

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

. .\Get-MrPSVersion.ps1

Вы также можете использовать полный путь к скрипту при выполнении его с использованием dot-sourcing.

. C:\Demo\Get-MrPSVersion.ps1

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

$Path = 'C:\'
. $Path\Get-MrPSVersion.ps1

Теперь, если вы проверите Function PSDrive, вы увидите, что функция Get-MrPSVersion доступна.

Get-ChildItem -Path Function:\Get-MrPSVersion
CommandType     Name                                               Version
-----------     ----                                               -------
Function        Get-MrPSVersion

Модули скриптов

В PowerShell модуль скрипта — это просто .psm1 файл, содержащий одну или несколько функций, как обычный скрипт, но с другим расширением файла.

Как создать модуль скрипта? Вы можете предположить, что команда называется примерно так New-Module. Это предположение является разумным предположением, но эта команда фактически создает динамический модуль, а не модуль скрипта.

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

help New-Module
NAME
    New-Module

SYNOPSIS
    Creates a new dynamic module that exists only in memory.


SYNTAX
    New-Module [-Name] <System.String> [-ScriptBlock]
    <System.Management.Automation.ScriptBlock> [-ArgumentList
    <System.Object[]>] [-AsCustomObject] [-Cmdlet <System.String[]>]
    [-Function <System.String[]>] [-ReturnResult] [<CommonParameters>]


DESCRIPTION
    The `New-Module` cmdlet creates a dynamic module from a script block.
    The members of the dynamic module, such as functions and variables, are
    immediately available in the session and remain available until you
    close the session.

    Like static modules, by default, the cmdlets and functions in a dynamic
    module are exported and the variables and aliases are not. However, you
    can use the Export-ModuleMember cmdlet and the parameters of
    `New-Module` to override the defaults.

    You can also use the **AsCustomObject** parameter of `New-Module` to return
    the dynamic module as a custom object. The members of the modules, such
    as functions, are implemented as script methods of the custom object
    instead of being imported into the session.

    Dynamic modules exist only in memory, not on disk. Like all modules,
    the members of dynamic modules run in a private module scope that is a
    child of the global scope. Get-Module cannot get a dynamic module, but
    Get-Command can get the exported members.

    To make a dynamic module available to `Get-Module`, pipe a `New-Module`
    command to Import-Module, or pipe the module object that `New-Module`
    returns to `Import-Module`. This action adds the dynamic module to the
    `Get-Module` list, but it does not save the module to disk or make it
    persistent.


RELATED LINKS
    Online Version: https://learn.microsoft.com/powershell/module/microsoft.
    powershell.core/new-module?view=powershell-5.1&WT.mc_id=ps-gethelp
    Export-ModuleMember
    Get-Module
    Import-Module
    Remove-Module
    about_Modules

REMARKS
    To see the examples, type: "Get-Help New-Module -Examples".
    For more information, type: "Get-Help New-Module -Detailed".
    For technical information, type: "Get-Help New-Module -Full".
    For online help, type: "Get-Help New-Module -Online"

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

В следующем примере используется командлет New-Module для создания динамического модуля в памяти, конкретно для демонстрации того, что происходит, если не использовать утвержденный глагол.

New-Module -Name MyModule -ScriptBlock {

    function Return-MrOsVersion {
        Get-CimInstance -ClassName Win32_OperatingSystem |
        Select-Object -Property @{Label='OperatingSystem';Expression={$_.Caption}}
    }

    Export-ModuleMember -Function Return-MrOsVersion

} | Import-Module
WARNING: The names of some imported commands from the module 'MyModule' include
unapproved verbs that might make them less discoverable. To find the commands with
unapproved verbs, run the Import-Module command again with the Verbose parameter. For a
list of approved verbs, type Get-Verb.

Хотя вы использовали командлет New-Module в предыдущем примере, как упоминалось ранее, это не команда для создания модулей сценариев в PowerShell.

Чтобы создать модуль скрипта, сохраните функции в .psm1 файле. Например, сохраните следующие две функции в файле с именем MyScriptModule.psm1.

function Get-MrPSVersion {
    $PSVersionTable
}

function Get-MrComputerName {
    $env:COMPUTERNAME
}

Попробуйте запустить одну из функций.

Get-MrComputerName

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

Get-MrComputerName : The term 'Get-MrComputerName' is not recognized as the
name of a cmdlet, function, script file, or operable program. Check the
spelling of the name, or if a path was included, verify that the path is
correct and try again.
At line:1 char:1
+ Get-MrComputerName
+ ~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (Get-MrComputerName:String) [
   ], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

Чтобы сделать функцию доступной, можно вручную импортировать MyScriptModule.psm1 файл с помощью командлета Import-Module .

Import-Module C:\MyScriptModule.psm1

PowerShell представила автоматическую загрузку модуля в версии 3. Чтобы воспользоваться этой функцией, модуль скрипта должен быть сохранен в папке с тем же базовым именем, что .psm1 и файл. Эта папка должна находиться в одном из каталогов, указанных в переменной $env:PSModulePath среды.

$env:PSModulePath

Выходные $env:PSModulePath данные трудно прочитать.

C:\Users\mike-ladm\Documents\WindowsPowerShell\Modules;C:\Program Files\Wind
owsPowerShell\Modules;C:\Windows\system32\WindowsPowerShell\v1.0\Modules;C:\
Program Files (x86)\Microsoft SQL Server\130\Tools\PowerShell\Modules\

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

$env:PSModulePath -split ';'

Первые три пути в списке — это расположения модулей по умолчанию. SQL Server Management Studio добавила последний путь при его установке.

C:\Users\mike-ladm\Documents\WindowsPowerShell\Modules
C:\Program Files\WindowsPowerShell\Modules
C:\Windows\system32\WindowsPowerShell\v1.0\Modules
C:\Program Files (x86)\Microsoft SQL Server\130\Tools\PowerShell\Modules\

Для автоматической загрузки модуля необходимо поместить MyScriptModule.psm1 файл в папку с именем MyScriptModule, и эта папка должна находиться непосредственно в одном из путей, перечисленных в
$env:PSModulePath.

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

Второй путь — это путь AllUsers , где я сохраняю все мои модули.

Третий путь указывает на C:\Windows\System32защищенное расположение системы. Только корпорация Майкрософт должна размещать модули там, так как она находится под структурой каталогов операционной системы.

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

Манифесты модуля

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

Манифест модуля можно создать с помощью командлета New-ModuleManifest . Единственным обязательным параметром является Path, но для правильной работы модуля необходимо также указать параметр RootModule .

Рекомендуется включить такие значения, как Author и Description, особенно если вы планируете опубликовать модуль в репозитории NuGet с помощью PowerShellGet. Эти поля необходимы в этом сценарии.

Один из быстрых способов определить, отсутствует ли у модуля манифест, — это проверить его версию.

Get-Module -Name MyScriptModule

Номер 0.0 версии является четким признаком отсутствия манифеста модуля.

ModuleType Version    Name                                ExportedCommands
---------- -------    ----                                ----------------
Script     0.0        MyScriptModule                      {Get-MrComputer...

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

$moduleManifestParams = @{
    Path = "$env:ProgramFiles\WindowsPowerShell\Modules\MyScriptModule\MyScriptModule.psd1"
    RootModule = 'MyScriptModule'
    Author = 'Mike F. Robbins'
    Description = 'MyScriptModule'
    CompanyName = 'mikefrobbins.com'
}

New-ModuleManifest @moduleManifestParams

Если при первоначальном создании манифеста модуля опустить какие-либо значения, можно добавить или обновить его позже с помощью командлета Update-ModuleManifest . Избегайте повторного создания манифеста после того, как вы создадите его, так как это создает новый GUID.

Определение общедоступных и частных функций

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

Если вы не следуете лучшим практикам и имеете только файл .psm1 без манифеста модуля, единственным вариантом является управление видимостью с помощью командлета Export-ModuleMember. Этот параметр позволяет явно определить, какие функции следует предоставлять непосредственно из .psm1 файла модуля скрипта, сохраняя все остальные закрытые по умолчанию.

В следующем примере только Get-MrPSVersion функция предоставляется пользователям модуля, а Get-MrComputerName функция остается доступной внутренне другим функциям в модуле.

function Get-MrPSVersion {
    $PSVersionTable
}

function Get-MrComputerName {
    $env:COMPUTERNAME
}

Export-ModuleMember -Function Get-MrPSVersion

Определите, какие команды доступны публично в модуле MyScriptModule .

Get-Command -Module MyScriptModule
CommandType     Name                                               Version
-----------     ----                                               -------
Function        Get-MrPSVersion                                    1.0

При добавлении манифеста модуля в модуль рекомендуется явно перечислить функции, которые необходимо экспортировать в разделе FunctionsToExport . Этот параметр позволяет контролировать, что вы показываете пользователям из файла манифеста модуля .psd1.

FunctionsToExport = 'Get-MrPSVersion'

Вам не нужно использовать и Export-ModuleMember в файле, и .psm1 в разделе FunctionsToExport манифеста модуля. Каждый подход достаточен сам по себе.

Сводка

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

Отзыв

  1. Как создать модуль скрипта в PowerShell?
  2. Почему важно использовать утвержденные глаголы в именах функций?
  3. Как создать манифест модуля в PowerShell?
  4. Каковы два способа экспорта только определенных функций из модуля?
  5. Какие условия необходимо выполнить для автоматической загрузки модуля при выполнении одной из ее команд?

Ссылки