はじめに
主にdynamicParam
キーワードを用いて、関数の引数にとる値(文字列)の種類を動的に制御することができる仕組みを、こちらのサイトでご紹介されております。
今回は、この仕組みを次の例のようにInvoke-Expression
コマンドレットと絡めてモジュールから利用しようとしたときに陥った落とし穴について述べていきます。
function B {
[CmdletBinding()]
param ($array)
dynamicParam {
$dp = New-DynamicParameter 'Fuga' -Mandatory -Position 0 | Add-ValidateSet $array
return New-DynamicParameterCollection $dp
}
process {
$Fuga = $PSBoundParameters.Fuga
return $Fuga
}
}
function Piyo($command) {
Invoke-Expression ($command -f "piyo")
}
スコープの誤設定?
早速ですが、次のコードをグローバルスコープで実行すると、エラーとなります。
Import-Module "$PSScriptRoot\Module" -Force
function A($a) {
Piyo 'B $a {0}'
}
A ("hoge", "huga", "piyo")
Add-ValidateSet: ...\Module1.psm1:5:84
Line |
5 | … namicParameter 'Fuga' -Mandatory -Position 0 | Add-ValidateSet $array
| ~~~~~~
| Cannot bind argument to parameter 'ValidateSet' because it is null.
New-DynamicParameterCollection: ...\Module1.psm1:6:47
Line |
6 | return New-DynamicParameterCollection $dp
| ~~~
| Cannot bind argument to parameter 'DynamicParameter' because it is null.
B: A positional parameter cannot be found that accepts argument 'piyo'.
これは、function B
の$array
引数に値1が渡されておらずnullとなっていることを意味しています。
なんでこうなるのか・・・試行錯誤した結果、どうやらスコープの設定に原因があることに辿り着きました。
function A
の$a
引数にscript
又はglobal
修飾子を添えれば、解決します。
Import-Module "$PSScriptRoot\Module" -Force
function A($script:a) {
Piyo 'B $a {0}'
}
A ("hoge", "huga", "piyo") # 出力:piyo
なお、function Piyo
が同一スコープにあるときや次のコードのように一体化しているときは、このエラーは発生しません。
function A($a) {
Invoke-Expression ('B $a {0}' -f "piyo")
}
Invoke-Expression
コマンドレットが別スコープに存在するときに発生するエラーということなのでしょうか・・・?
まとめ
今回はDynamicParameterを使ったときを題材にご紹介しましたが、もしかしたらIValidateSetValuesGeneratorインターフェースを利用したパラメータの定義でも同様の落とし穴が存在するかもしれません2。
とにかくダイナミックスコープというものが、ほぼJavaしかやったことのない人間にとってこんな苦しい概念だとは思いませんでした・・・orz