Kapitola 10 – Skriptovací moduly

Když v PowerShellu změníte one linery a skripty na opakovaně použitelné nástroje, bude ještě důležitější, pokud se jedná o něco, co budete často používat. Zabalení funkcí do modulu skriptu z nich dělá lepší vzhled a chování a usnadňuje jejich sdílení.

funkce Dot-Sourcing

Něco, o čem jsme nemluvili v předchozí kapitole, je funkce typu dot-sourcing. Pokud funkce ve skriptu není součástí modulu, jediným způsobem, jak ji načíst do paměti, je dot-source .PS1 soubor, do kterého je uložený.

Následující funkce byla uložena jako Get-MrPSVersion.ps1.

function Get-MrPSVersion {
    $PSVersionTable
}

Když skript spustíte, nic se nestane.

.\Get-MrPSVersion.ps1

Pokud se pokusíte funkci volat, vygeneruje chybovou zprávu.

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) [], CommandNotFou
   ndException
    + FullyQualifiedErrorId : CommandNotFoundException

Pokud chcete zjistit, jestli jsou funkce načtené do paměti, zkontrolujte, jestli na webu PsDrive funkce existují.

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.GetChildItemCommand

Problém s voláním skriptu, který obsahuje funkci, je, že se funkce načtou do oboru skriptu . Po dokončení skriptu se tento obor odebere a funkce se s ním odebere.

Funkce musí být načtena do globálního oboru. Toho lze dosáhnout pomocí dot-sourcing skriptu, který obsahuje funkci. Relativní cestu lze použít.

. .\Get-MrPSVersion.ps1

Lze použít také plně kvalifikovanou cestu.

. C:\Demo\Get-MrPSVersion.ps1

Pokud je část cesty uložená v proměnné, lze ji kombinovat se zbytkem cesty. Není důvod použít zřetězení řetězců ke zkombinování proměnné spolu se zbytkem cesty.

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

Když teď zkontroluji funkci PSDrive, Get-MrPSVersion funkce existuje.

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

Moduly skriptů

Modul skriptu v PowerShellu je jednoduše soubor obsahující jednu nebo více funkcí, které jsou uložené jako .PSM1 soubor místo .PS1 souboru.

Jak vytvoříte modul skriptu? Pravděpodobně hádáte příkazem s názvem něco jako New-Module. Váš předpoklad by byl chybný. I když je v PowerShellu příkaz s názvem New-Module, tento příkaz vytvoří dynamický modul, nikoli modul skriptu. Vždy si nezapomeňte přečíst nápovědu k příkazu, i když si myslíte, že jste příkaz našli, který potřebujete.

help New-Module
NAME
    New-Module

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

SYNTAX
    New-Module [-Name] <String> [-ScriptBlock] <ScriptBlock> [-ArgumentList <Object[]>]
    [-AsCustomObject] [-Cmdlet <String[]>] [-Function <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: http://go.microsoft.com/fwlink/?LinkId=821495
    Export-ModuleMember
    Get-Module
    Import-Module
    Remove-Module

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"

V předchozí kapitole jsem zmínil, že funkce by měly používat schválené příkazy, jinak při importu modulu vygenerují upozornění. Následující kód používá rutinu New-Module k vytvoření dynamického modulu v paměti. Tento modul ukazuje neschválené upozornění sloves.

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.

Stačí zopakovat, i když New-Module se rutina použila v předchozím příkladu, nejedná se o příkaz pro vytváření modulů skriptů v PowerShellu.

Uložte následující dvě funkce do souboru s názvem MyScriptModule.psm1.

function Get-MrPSVersion {
    $PSVersionTable
}

function Get-MrComputerName {
    $env:COMPUTERNAME
}

Zkuste volat jednu z těchto funkcí.

Get-MrComputerName
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) [], CommandNot
   FoundException
    + FullyQualifiedErrorId : CommandNotFoundException

Vygeneruje se chybová zpráva s oznámením, že se funkce nenašla. Můžete také zkontrolovat funkci PSDrive stejně jako předtím a zjistíte, že neexistuje ani tam.

Soubor můžete importovat ručně pomocí rutiny Import-Module .

Import-Module C:\MyScriptModule.psm1

Funkce automatického načítání modulu byla představena v PowerShellu verze 3. Aby bylo možné využít automatické načítání modulu, musí být modul skriptu uložen ve složce se stejným základním názvem jako .PSM1 soubor a v umístění zadaném v $env:PSModulePath.

$env:PSModulePath
C:\Users\mike-ladm\Documents\WindowsPowerShell\Modules;C:\Program Files\WindowsPowerShell\
Modules;C:\Windows\system32\WindowsPowerShell\v1.0\Modules;C:\Program Files (x86)\Microsof
t SQL Server\130\Tools\PowerShell\Modules\

Výsledky jsou obtížně čitelné. Vzhledem k tomu, že cesty jsou oddělené středníkem, můžete výsledky rozdělit, abyste vrátili každou cestu na samostatném řádku. Díky tomu se snadněji čtou.

$env:PSModulePath -split ';'
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\

První tři cesty v seznamu jsou výchozí. Po instalaci SQL Server Management Studio se přidala poslední cesta. Aby automatické načítání modulu fungovalo, MyScriptModule.psm1 musí být soubor umístěný ve složce s názvem MyScriptModule přímo v jedné z těchto cest.

Ne tak rychle. Pro mě není moje aktuální cesta uživatele první v seznamu. Téměř nikdy tuto cestu nepoužívám, protože se přihlašuji k Windows s jiným uživatelem než ten, který používám ke spuštění PowerShellu. To znamená, že se nenachází v normální složce Dokumenty.

Druhá cesta je cesta AllUsers . Toto je umístění, kam ukládám všechny moduly.

Třetí cesta je pod C:\Windows\System32. Moduly v daném umístění by měl ukládat jenom Microsoft, protože se nachází ve složce operačních systémů.

.PSM1 Jakmile se soubor nachází ve správné cestě, modul se automaticky načte, když se zavolá jeden z jeho příkazů.

Manifesty modulů

Všechny moduly by měly mít manifest modulu. Manifest modulu obsahuje metadata o vašem modulu. Přípona souboru manifestu modulu je .PSD1. Ne všechny soubory s příponou .PSD1 jsou manifesty modulů. Dají se použít také pro věci, jako je uložení části prostředí konfigurace DSC. New-ModuleManifest slouží k vytvoření manifestu modulu. Cesta je jediná požadovaná hodnota. Pokud ale není zadaný rootModule , modul nebude fungovat. V případě, že se rozhodnete nahrát modul do úložiště NuGet pomocí modulu PowerShellGet, je vhodné zadat autor a popis , protože tyto hodnoty jsou v tomto scénáři povinné.

Verze modulu bez manifestu je 0.0. To je mrtvý dárek, že modul nemá manifest.

Get-Module -Name MyScriptModule
ModuleType Version    Name                                ExportedCommands
---------- -------    ----                                ----------------
Script     0.0        myscriptmodule                      {Get-MrComputerName, Get-MrP...

Manifest modulu lze vytvořit se všemi doporučenými informacemi.

New-ModuleManifest -Path $env:ProgramFiles\WindowsPowerShell\Modules\MyScriptModule\MyScriptModule.psd1 -RootModule MyScriptModule -Author 'Mike F Robbins' -Description 'MyScriptModule' -CompanyName 'mikefrobbins.com'

Pokud některé z těchto informací při počátečním vytvoření manifestu modulu chybí, můžete je později přidat nebo aktualizovat pomocí Update-ModuleManifest. Manifest znovu nevytvářejte, New-ModuleManifest jakmile už je vytvořený, protože identifikátor GUID se změní.

Definování veřejných a privátních funkcí

Možná máte pomocné funkce, které můžou být soukromé a přístupné jenom jinými funkcemi v modulu. Nemají být přístupné uživatelům vašeho modulu. Existuje několik různých způsobů, jak toho dosáhnout.

Pokud nepoužíváte osvědčené postupy a máte .PSM1 jenom soubor, jedinou možností je použít tuto rutinu Export-ModuleMember .

function Get-MrPSVersion {
    $PSVersionTable
}

function Get-MrComputerName {
    $env:COMPUTERNAME
}

Export-ModuleMember -Function Get-MrPSVersion

V předchozím příkladu je uživatelům vašeho modulu k dispozici pouze Get-MrPSVersion funkce, ale Get-MrComputerName funkce je k dispozici pro další funkce v rámci samotného modulu.

Get-Command -Module MyScriptModule

CommandType     Name                        Version    Source
-----------     ----                        -------    ------
Function        Get-MrPSVersion             1.0        MyScript...

Pokud jste do modulu přidali manifest modulu (a měli byste), doporučujeme zadat jednotlivé funkce, které chcete exportovat v části FunctionsToExport manifestu modulu.

FunctionsToExport = 'Get-MrPSVersion'

Není nutné použít Export-ModuleMember jak v souboru, tak v .PSM1 části FunctionsToExport manifestu modulu. Jeden nebo druhý je dostačující.

Souhrn

V této kapitole jste se naučili, jak v PowerShellu převést funkce na modul skriptu. Seznámili jste se také s některými osvědčenými postupy pro vytváření modulů skriptů, jako je například vytvoření manifestu modulu modulu pro skript.

Opakování

  1. Jak vytvoříte modul skriptu v PowerShellu?
  2. Proč je důležité, aby vaše funkce používaly schválené příkazy?
  3. Jak vytvoříte manifest modulu v PowerShellu?
  4. Jaké jsou dvě možnosti exportu pouze určitých funkcí z modulu?
  5. Co je potřeba k automatickému načtení modulů při zavolání příkazu?