11. モジュール

11.1 はじめに

§3.14 で説明されているように、モジュールは、PowerShell コードをパーティション分割、整理、抽象化するための再利用可能な自己完結型の単位です。 モジュールには、1 つ以上の "モジュール メンバー" (コマンドレットや関数などのコマンドと、変数やエイリアスなどの項目) を含めることができます。 これらのメンバーの名前は、モジュールに対してプライベートに保持することも、"インポート" されるセッションに "エクスポート" することもできます。

モジュールには、マニフェスト、スクリプト、バイナリの 3 "種類" があります。 "マニフェスト モジュール" は、モジュールに関する情報が含まれているファイルで、そのモジュールの使用に関する特定の側面を制御します。 "スクリプト モジュール" は、ファイル拡張子が .ps1 ではなく .psm1 の PowerShell スクリプト ファイルです。 "バイナリ モジュール" には、コマンドレットとプロバイダーを定義するクラス型が含まれます。 スクリプト モジュールとは異なり、バイナリ モジュールはコンパイル済みの言語で記述されます。 バイナリ モジュールは、この仕様の対象外です。

バイナリ モジュールは、PowerShell ライブラリに対してコンパイルされた .NET アセンブリ (つまり、DLL) です。

モジュールは "入れ子" にすることができます。つまり、あるモジュールにより別のモジュールをインポートできます。 入れ子になったモジュールが関連付けられているモジュールは、"ルート モジュール" です。

PowerShell セッションが作成されると、既定では、モジュールはインポートされません。

モジュールがインポートされると、それらの場所の特定に使用される検索パスは、環境変数 PSModulePath によって定義されます。

次のコマンドレットは、モジュールを処理します。

  • Get-Module: インポートされたモジュールまたはインポート可能なモジュールを識別します。
  • Import-Module: 1 つ以上のモジュールを現在のセッションに追加します (§11.4 を参照)
  • Export-ModuleMember: エクスポートするモジュール メンバーを識別します。
  • Remove-Module: 1 つ以上のモジュールを現在のセッションから削除します (§11.5 を参照)
  • New-Module: 動的モジュールを作成します (§11.7 を参照)

11.2 スクリプト モジュールの記述

スクリプト モジュールは、スクリプト ファイルです。 次のスクリプト モジュールについて考えてみます。

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

このモジュールには、それぞれ 1 つのエイリアスを持つ 2 つの関数が含まれています。 既定では、すべての関数名と、関数名のみがエクスポートされます。 ただし、コマンドレット Export-ModuleMember を使用して何かをエクスポートしている場合、明示的にエクスポートされたこれらのものだけがエクスポートされます。 一連のコマンドと項目は、1 回の呼び出しで、またはこのコマンドレットの複数の呼び出しでエクスポートできます。このような呼び出しは、現在のセッションの累積です。

11.3 スクリプト モジュールのインストール

スクリプト モジュールはスクリプト ファイルで定義されており、モジュールは任意のディレクトリに格納できます。 環境変数 PSModulePath は、モジュール関連のコマンドレットが名前に完全修飾パスを含まないモジュールを検索するときに、検索されるディレクトリのセットをポイントします。 追加の参照パスを指定できます。例:

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

パスが追加されると、現在のセッションのみに影響します。

または、モジュールがインポートされるときに完全修飾パスを指定することもできます。

11.4 スクリプト モジュールのインポート

モジュール内のリソースを使用する前に、そのモジュールを現在のセッションにインポートする必要があります。そのためには、コマンドレット Import-Module を使用します。 Import-Module は、自身が実際にインポートするリソースを制限できます。

モジュールがインポートされると、そのスクリプト ファイルが実行されます。 このプロセスを構成するには、スクリプト ファイルで 1 つ以上のパラメーターを定義し、Import-Module の ArgumentList パラメーターを使用して対応する引数を渡します。

次のスクリプトでは、§11.2 で定義されているこれらの関数やエイリアスを使用しています。

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

"0 degrees C is " + (Convert-CentigradeToFahrenheit 0) + " 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"

モジュールのコマンドまたは項目の名前がセッションのコマンドまたは項目の名前と同じとき、モジュールをインポートすると名前の競合が発生します。 名前が競合すると、名前が隠されたり、置換されたりします。 Import-Module の Prefix パラメーターを使用して、名前の競合を回避できます。 また、AliasCmdletFunction、および Variable パラメーターを使用して、インポートするコマンドだけを選択することもできます。これにより、名前競合の可能性が減ります。

コマンドが隠れているとしても、出所のモジュールの名前でその名前を修飾すればコマンドを実行できます。 たとえば、& M\F 100 を使うと、モジュール M で関数 F が呼び出され、それが引数 100 に渡されます。

セッションに種類と名前が同じコマンドが含まれるとき、たとえば、2 つのコマンドレットの名前が同じ場合、既定では追加された日が新しいほうのコマンドが実行されます。

モジュールに関連するスコープの詳細については、「§3.5.6」を参照してください。

11.5 スクリプト モジュールの削除

コマンドレット Remove-Module を使用して、1 つ以上のモジュールをセッションから削除できます。

モジュールを削除してもモジュールはアンインストールされません。

スクリプト モジュールでは、次のように、そのモジュールの削除の前に実行されるコードを指定できます。

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

11.6 モジュール マニフェスト

§11.1 で説明されているように、マニフェスト モジュールは、モジュールに関する情報が含まれているファイルで、そのモジュールの使用に関する特定の側面を制御します。

モジュールには対応するマニフェストは必要ありませんが、もしある場合は、そのマニフェストの名前は、ファイル名拡張子が .psd1 であることを除き、それによって説明されるモジュールと同じ名前になります。

マニフェストには、制限された数の PowerShell スクリプトのサブセットが含まれています。このスクリプトにより、一連のキーを含むハッシュテーブルが返されます。 これらのキーとその値により、そのモジュールの "マニフェスト要素" が指定されます。 つまり、これらによりモジュールの内容や属性が説明され、前提条件が定義され、コンポーネントの処理方法が決定されます。

基本的に、マニフェストはデータ ファイルです。ただし、データ型、if ステートメント、および算術演算子と比較演算子への参照を含めることができます (代入、関数の定義、およびループは許可されていません)。マニフェストには、環境変数に対する読み取りアクセス権もあります。また、コマンドレット Join-Path への呼び出しを含めることができるため、パスを構築できます。

注意

エディターの注: 元のドキュメントには、モジュール マニフェスト ファイルで許可されているキーの一覧が含まれています。 この一覧は古く、完全なものではありません。 モジュール マニフェストのすべてのキーの一覧については、「New-ModuleManifest」を参照してください。

必要なキーは ModuleVersion のみです。

簡単なマニフェストの例を次に示します。

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

キー GUID は値 string を持ちます。 これにより、モジュールのグローバル一意識別子 (GUID) が指定されます。 GUID は、同じ名前のモジュールを区別するために使用できます。 新しい GUID を作成するには、メソッド [guid]::NewGuid() を呼び出します。

11.7 動的モジュール

"動的モジュール" は、コマンドレット New-Module により実行時にメモリに作成されるモジュールです。ディスクから読み込まれません。 次の例を確認してください。

$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

スクリプト ブロック $sb により、モジュールの内容 (この場合、これらの関数への 2 つの関数と 2 つのエイリアス) が定義されます。 ディスク上のモジュールと同様、関数のみが既定でエクスポートされるため、Export-ModuleMember コマンドレット呼び出しは、関数とエイリアスの両方をエクスポートするためにあります。

New-Module が実行されると、Convert-CentigradeToFahrenheit と c2f の呼び出しで示されているように、エクスポートされた 4 つの名前がセッションで使用できるようになります。

すべてのモジュール同様、動的モジュールのメンバーはグローバル スコープの子であるプライベート モジュール スコープで実行されます。 Get-Module は動的モジュールを取得できませんが、Get-Command はエクスポートされたメンバーを取得できます。

Get-Module で動的モジュールを使用できるようにするには、パイプを使用して、New-Module コマンドを Import-Module に渡すか、パイプを使用して、New-Module によって返されるモジュール オブジェクトを Import-Module に渡します。 このアクションでは、Get-Module の一覧に動的モジュールが追加されますが、モジュールはディスクには保存されず、永続的になることもありません。

11.8 クロージャ

動的モジュールは、データがアタッチされた関数である "クロージャ" を作成するために使用できます。 次の例を確認してください。

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

ここでの目的は、Get-NextID が、開始値を指定できるシーケンスの次の ID を返すことです。 ただし、複数のシーケンスがサポートされており、それぞれが独自の $startValue および $nextID コンテキストを持っている必要があります。 これは、メソッド [scriptblock]::GetNewClosure の呼び出しによって達成されます (§4.3.7)。

新しいクロージャが GetNewClosure によって作成されるたびに、新しい動的モジュールが作成され、呼び出し元のスコープ内の変数 (この場合は、インクリメントを含むスクリプト ブロック) がこの新しいモジュールにコピーされます。 親関数内 (ただし、スクリプト ブロックの外側) で定義された nextId がインクリメントされるようにするには、明示的な script: スコープ プレフィックスが必要です。

もちろん、スクリプト ブロックは名前付き関数である必要はありません。例:

$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