11. Moduły

11.1 Wprowadzenie

Zgodnie z słowem w module (3.14) moduł jest samodzielną jednostką wielokrotnego użytku, która umożliwia partycjonowanie, zorganizowanie i abstraktowanie kodu programu PowerShell. Moduł może zawierać jeden lub więcej elementów członkowskich modułu, które są poleceniami (takimi jak polecenia cmdlet i funkcje) i elementami (takimi jak zmienne i aliasy). Nazwy tych elementów członkowskich mogą być przechowywane jako prywatne dla modułu lub mogą być eksportowane do sesji, do której moduł jest importowany.

Istnieją trzy różne typy modułów: manifest, skrypt i plik binarny. Moduł manifestu to plik, który zawiera informacje o module i kontroluje niektóre aspekty użycia tego modułu. Moduł skryptu to plik skryptu programu PowerShell z rozszerzeniem pliku .psm1 .ps1zamiast . Moduł binarny zawiera typy klas, które definiują polecenia cmdlet i dostawców. W przeciwieństwie do modułów skryptów moduły binarne są pisane w skompilowanych językach. Moduły binarne nie są objęte tą specyfikacją.

Moduł binarny jest zestawem .NET (tj. biblioteką DLL), który został skompilowany względem bibliotek programu PowerShell.

Moduły mogą być zagnieżdżone; oznacza to, że jeden moduł może zaimportować inny moduł. Moduł, który ma skojarzone moduły zagnieżdżone, jest modułem głównym.

Po utworzeniu sesji programu PowerShell domyślnie nie są importowane żadne moduły.

Podczas importowania modułów ścieżka wyszukiwania używana do ich lokalizowania jest definiowana przez zmienną środowiskową PSModulePath.

Następujące polecenia cmdlet zajmują się modułami:

11.2 Pisanie modułu skryptu

Moduł skryptu to plik skryptu. Rozważmy następujący moduł skryptu:

function Convert-CentigradeToFahrenheit ([double]$tempC) {
    return ($tempC * (9.0 / 5.0)) + 32.0
}
New-Alias c2f Convert-CentigradeToFahrenheit

function Convert-FahrenheitToCentigrade ([double]$tempF) {
    return ($tempF - 32.0) * (5.0 / 9.0)
}
New-Alias f2c Convert-FahrenheitToCentigrade

Export-ModuleMember -Function Convert-CentigradeToFahrenheit
Export-ModuleMember -Function Convert-FahrenheitToCentigrade
Export-ModuleMember -Alias c2f, f2c

Ten moduł zawiera dwie funkcje, z których każda ma alias. Domyślnie eksportowane są wszystkie nazwy funkcji i tylko nazwy funkcji. Jednak gdy polecenie cmdlet Export-ModuleMember zostanie użyte do wyeksportowania czegokolwiek, zostaną wyeksportowane tylko te, które zostały wyeksportowane jawnie. Szereg poleceń i elementów można wyeksportować w jednym wywołaniu lub w wielu wywołaniach tego polecenia cmdlet. Takie wywołania są kumulowane dla bieżącej sesji.

11.3 Instalowanie modułu skryptu

Moduł skryptu jest zdefiniowany w pliku skryptu, a moduły mogą być przechowywane w dowolnym katalogu. Zmienna środowiskowa PSModulePath wskazuje zestaw katalogów, które mają być przeszukiwane, gdy polecenia cmdlet związane z modułem wyszukają moduły, których nazwy nie zawierają w pełni kwalifikowanej ścieżki. Można określić dodatkowe ścieżki wyszukiwania; na przykład

$Env:PSModulepath = $Env:PSModulepath + ";<additional-path>"

Wszelkie dodatkowe dodane ścieżki wpływają tylko na bieżącą sesję.

Alternatywnie można określić w pełni kwalifikowaną ścieżkę podczas importowania modułu.

11.4 Importowanie modułu skryptu

Aby można było użyć zasobów w module, należy zaimportować ten moduł do bieżącej sesji przy użyciu polecenia cmdlet Import-Module. Import-Module może ograniczyć zasoby, które faktycznie importuje.

Podczas importowania modułu jest wykonywany jego plik skryptu. Ten proces można skonfigurować, definiując co najmniej jeden parametr w pliku skryptu i przekazując odpowiednie argumenty za pośrednictwem parametru ArgumentList .Import-Module

Rozważmy następujący skrypt, który używa tych funkcji i aliasów zdefiniowanych w .11.2:

Import-Module "E:\Scripts\Modules\PSTestTemperature_" -Verbose

"0 degrees C is &quot; + (Convert-CentigradeToFahrenheit 0) + &quot; degrees F"
"100 degrees C is " + (c2f 100) + " degrees F"
"32 degrees F is " + (Convert-FahrenheitToCentigrade 32) + " degrees C"
"212 degrees F is " + (f2c 212) + " degrees C"

Importowanie modułu powoduje konflikt nazw, gdy polecenia lub elementy w module mają takie same nazwy jak polecenia lub elementy w sesji. Konflikt nazw powoduje, że nazwa jest ukryta lub zastąpiona. Prefiks parametru może Import-Module służyć do uniknięcia konfliktów nazw. Ponadto parametry Alias, Cmdlet, Function i Variable mogą ograniczać wybór poleceń do zaimportowania, co zmniejsza prawdopodobieństwo konfliktu nazw.

Nawet jeśli polecenie jest ukryte, można je uruchomić, kwalifikując jego nazwę do nazwy modułu, z którego pochodzi. Na przykład funkcja & M\F 100 F w module M jest wywoływana i przekazuje do niego argument 100.

Gdy sesja zawiera polecenia tego samego rodzaju o tej samej nazwie, takie jak dwa polecenia cmdlet o tej samej nazwie, domyślnie uruchamia ostatnio dodane polecenie.

Zobacz moduł .3.5.6 , aby uzyskać omówienie zakresu w odniesieniu do modułów.

11.5 Usuwanie modułu skryptu

Co najmniej jeden moduł można usunąć z sesji za pomocą polecenia cmdlet Remove-Module.

Usunięcie modułu nie powoduje odinstalowania modułu.

W module skryptu można określić kod, który ma zostać wykonany przed usunięciem tego modułu, w następujący sposób:

$MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = { *on-removal-code* }

11.6 Manifesty modułów

Jak określono w module manifestu ,11.1, jest to plik, który zawiera informacje o module i kontroluje niektóre aspekty użycia tego modułu.

Moduł nie musi mieć odpowiadającego mu manifestu, ale jeśli tak, ma taką samą nazwę jak moduł, który opisuje, ale z rozszerzeniem .psd1 pliku.

Manifest zawiera ograniczony podzbiór skryptu programu PowerShell, który zwraca tabelę skrótów zawierającą zestaw kluczy. Te klucze i ich wartości określają elementy manifestu dla tego modułu. Oznacza to, że opisują zawartość i atrybuty modułu, definiują wymagania wstępne i określają sposób przetwarzania składników.

Zasadniczo manifest jest plikiem danych; Może jednak zawierać odwołania do typów danych, instrukcji if oraz operatorów arytmetycznych i porównania. (Przypisania, definicje funkcji i pętle nie są dozwolone). Manifest ma również dostęp do odczytu zmiennych środowiskowych i może zawierać wywołania polecenia cmdlet Join-Path, aby można było konstruować ścieżki.

Uwaga

Uwaga edytora: oryginalny dokument zawiera listę kluczy dozwolonych w pliku manifestu modułu. Ta lista jest nieaktualna i niekompletna. Aby uzyskać pełną listę kluczy w manifeście modułu, zobacz New-ModuleManifest.

Jedynym wymaganym kluczem jest ModuleVersion.

Oto przykład prostego manifestu:

@{
ModuleVersion = '1.0'
Author = 'John Doe'
RequiredModules = @()
FunctionsToExport = 'Set*','Get*','Process*'
}

Identyfikator GUID klucza ma string wartość . Określa globalnie unikatowy identyfikator IDentifier (GUID) dla modułu. Identyfikator GUID może służyć do rozróżniania modułów o tej samej nazwie. Aby utworzyć nowy identyfikator GUID, wywołaj metodę [guid]::NewGuid().

11.7 Moduły dynamiczne

Moduł dynamiczny to moduł , który jest tworzony w pamięci w czasie wykonywania przez polecenie cmdlet New-Module; nie jest ładowany z dysku. Rozpatrzmy następujący przykład:

$sb = {
    function Convert-CentigradeToFahrenheit ([double]$tempC) {
        return ($tempC * (9.0 / 5.0)) + 32.0
    }

    New-Alias c2f Convert-CentigradeToFahrenheit

    function Convert-FahrenheitToCentigrade ([double]$tempF) {
        return ($tempF - 32.0) * (5.0 / 9.0)
    }

    New-Alias f2c Convert-FahrenheitToCentigrade

    Export-ModuleMember -Function Convert-CentigradeToFahrenheit
    Export-ModuleMember -Function Convert-FahrenheitToCentigrade
    Export-ModuleMember -Alias c2f, f2c
}

New-Module -Name MyDynMod -ScriptBlock $sb
Convert-CentigradeToFahrenheit 100
c2f 100

Blok skryptu $sb definiuje zawartość modułu, w tym przypadku dwie funkcje i dwa aliasy dla tych funkcji. Podobnie jak w przypadku modułu na dysku domyślnie eksportowane są tylko funkcje, Export-ModuleMember więc istnieją wywołania cmdlet w celu wyeksportowania zarówno funkcji, jak i aliasów.

Po New-Module jego zakończeniu cztery wyeksportowane nazwy są dostępne do użycia w sesji, jak pokazano w wywołaniach do Convert-CentigradeToFahrenheit i c2f.

Podobnie jak wszystkie moduły, elementy członkowskie modułów dynamicznych działają w zakresie modułu prywatnego, który jest podrzędnym zakresem globalnym. Get-Module nie może pobrać modułu dynamicznego, ale Get-Command może pobrać wyeksportowane elementy członkowskie.

Aby udostępnić moduł dynamiczny Get-Module, należy potokować Import-Module``New-Module polecenie do polecenia lub potok obiektu modułu, który zwraca New-Module wartość , do polecenia Import-Module. Ta akcja powoduje dodanie modułu dynamicznego do Get-Module listy, ale nie powoduje zapisania modułu na dysku ani jego trwałego działania.

11.8 Zamknięcia

Za pomocą modułu dynamicznego można utworzyć zamknięcie, funkcję z dołączonymi danymi. Rozpatrzmy następujący przykład:

function Get-NextID ([int]$startValue = 1) {
    $nextID = $startValue
    {
        ($script:nextID++)
    }.GetNewClosure()
}

$v1 = Get-NextID      # get a scriptblock with $startValue of 0
& $v1                 # invoke Get-NextID getting back 1
& $v1                 # invoke Get-NextID getting back 1

$v2 = Get-NextID 100  # get a scriptblock with $startValue of 100
& $v2                 # invoke Get-NextID getting back 100
& $v2                 # invoke Get-NextID getting back 101

Intencją w tym miejscu jest zwrócenie Get-NextID następnego identyfikatora w sekwencji, której wartość rozpoczęcia można określić. Jednak musi być obsługiwanych wiele sekwencji, z których każda ma własny kontekst $startValue i $nextID . Jest to osiągane przez wywołanie metody [scriptblock]::GetNewClosure (%).4.3.7).

GetNewClosureZa każdym razem, gdy nowe zamknięcie jest tworzone przez program , tworzony jest nowy moduł dynamiczny, a zmienne w zakresie wywołującym (w tym przypadku blok skryptu zawierający przyrost) są kopiowane do tego nowego modułu. Aby upewnić się, że wartość nextId zdefiniowana wewnątrz funkcji nadrzędnej (ale poza blokiem skryptu) jest zwiększana, wymagany jest jawny skrypt: prefiks zakresu.

Oczywiście blok skryptu nie musi być nazwaną funkcją; na przykład:

$v3 = & {      # get a scriptblock with $startValue of 200
    param ([int]$startValue = 1)
    $nextID = $startValue
    {
        ($script:nextID++)
    }.GetNewClosure()
} 200

& $v3          # invoke script getting back 200
& $v3          # invoke script getting back 201