からめもぶろぐ。

俺たちは雰囲気で OAuth をやっている

PowerShell モジュールで変数をエクスポートするときの挙動について

この記事は PowerShell Advent Calendar 2019 に参加しています。

qiita.com

PowerShell モジュールで変数をエクスポートするときの挙動について、ちょっとハマりどころがあったので、整理してみたいと思います。

スクリプトで Export-ModuleMember を呼び出さない場合

まずは Export-ModuleMember を呼び出さない場合を見てみます。「HelloWorld.psm1」という名前でファイルを作成し、各スコープごとに変数を宣言します。

$private:HelloWorldPrivate = 'HelloWorld'
$local:HelloWorldLocal = 'HelloWorld'
$script:HelloWorldScript = 'HelloWorld'
$global:HelloWorldGlobal = 'HelloWorld'

このモジュールを読み込んだときに使用できる変数は以下のようになります。

PS C:\> Import-Module .\HelloWorld.psm1
PS C:\> Get-ChildItem variable: | where Name -like 'HelloWorld*'

Name                           Value
----                           -----
HelloWorldGlobal               HelloWorld

global スコープで宣言した変数以外は使用できないことがわかります。

スクリプトで Export-ModuleMember を呼び出す場合

先ほどの「HelloWorld.psm1」を少しいじって Export-ModuleMember を追加します。

$private:HelloWorldPrivate = 'HelloWorld'
$local:HelloWorldLocal = 'HelloWorld'
$script:HelloWorldScript = 'HelloWorld'
$global:HelloWorldGlobal = 'HelloWorld'

Export-ModuleMember -Variable '*'

同じようにモジュールを読み込みます。

PS C:\> Import-Module .\HelloWorld.psm1
PS C:\> Get-ChildItem variable: | where Name -like 'HelloWorld*'

Name                           Value
----                           -----
HelloWorldGlobal               HelloWorld
HelloWorldLocal                HelloWorld
HelloWorldPrivate              HelloWorld
HelloWorldScript               HelloWorld

すべてのスコープについて変数がエクスポートされていることがわかります。

マニフェストで VariablesToExport を指定しない場合

スクリプト ファイル (.psm1) を直接モジュールとして読み込むのではなく、マニフェスト ファイル (.psd1) を使うことがほとんどだと思いますので、こちらの動作もみてみます。まずは VariablesToExport を指定しない、つまり空の配列を指定した状態で試してみます。

@{
RootModule = 'HelloWorld.psm1'
ModuleVersion = '1.0'
VariablesToExport = @()
}

この状態でモジュールを読み込みます。

PS C:\> Import-Module .\HelloWorld.psd1
PS C:\> Get-ChildItem variable: | where Name -like 'HelloWorld*'

Name                           Value
----                           -----
HelloWorldGlobal               HelloWorld

Export-ModuleMember と同様に global スコープで宣言した変数のみが使用できます。

マニフェストで VariablesToExport を指定した場合

それでは VariablesToExport を指定してみます。

@{
RootModule = 'HelloWorld.psm1'
ModuleVersion = '1.0'
VariablesToExport = '*'
}

同様にモジュールを読み込みます。

PS C:\> Import-Module .\HelloWorld.psd1
PS C:\> Get-ChildItem variable: | where Name -like 'HelloWorld*'

Name                           Value
----                           -----
HelloWorldGlobal               HelloWorld

Export-ModuleMember のときとは違って、global 以外の変数がエクスポートされていません。

まとめ

一通り試してみて以下のことがわかりました。

  • Export-ModuleMember を呼び出さなくても global スコープの変数は使用できる
  • Export-ModuleMember を呼び出すとどのスコープでも変数はエクスポートされる
  • マニフェストの VariablesToExport は無視される

じゃあ VariablesToExport は何をするんだって話。バイナリ モジュールでも変数をエクスポートすることはできないので謎は深まります。いずれにしても変数をエクスポートする場合は明示的に Export-ModuleMember を呼び出しましょう。

(2019/12/18 追記)
Twitter で指摘をいただきました。

ということだそうですよ。