PowerShell ValidateSetの動的生成
PowerShellのValidateSetの動的生成
概要
ValidateSet
は以下の効果を持ちます。
- 引数の入力値候補の自動補完
- 許される入力値の候補以外の排除
通常は、以下のようにべた書きで配列を入力します。
[ValidateSet("A", "B", "C")]
今回は、発展的内容として、
許容入力値の配列を動的に生成する方法と、
それができないときの代替案を紹介します。
動的生成
IValidateSetValuesGenerator
を実装したクラスを定義し、その中で入力値リストを生成するGetValidValues()
メソッドを定義する。(PowerShell Core限定)
代替案
[ArgumentCompleter({許容値配列生成})]
と[ValidateScript({$_ -in $(許容値配列生成)})]
を組み合わせる(PowerShell5以降)
動的生成
PowerShell Core以降なら、特別な理由がない限りこの方法を使います。
フォーマット
using namespace System.Management.Automation
class 動的生成するValidateSetのクラス名 : IValidateSetValuesGenerator {
[string[]] GetValidValues() {
return $(配列生成処理)
}
}
function 関数名{
param(
[ValidateSet([動的生成するValidateSetのクラス名])]
[型] $引数名
)
}
コード例
using namespace System.Management.Automation
class PsScriptRelativePaths : IValidateSetValuesGenerator {
[string[]] GetValidValues() {
return $(Resolve-Path -Relative -LiteralPath $(Get-ChildItem -Path "./" -Filter "*.ps1").FullName)
}
}
function Invoke-PsScript{
<#
.SYNOPSIS
PowerShellScriptを実行します
.DESCRIPTION
この関数を使うと、以下のメリットがあります:
1. PowerShellのスクリプトパスのみ補完してくれる
一方備え付けのドット演算子には以下のデメリットがあります:
1. PowerShellのスクリプト以外のパスを補完してしまう
関数別名は、以下のようになっています:
1. `..` -> ドット演算子との類似性を保つため
2. `Invoke-Script` -> PowerShell内でのScriptは基本PowerShellスクリプトのため
.PARAMETER Path
人間にとって扱いやすい相対パスで、
現在地直下のスクリプトを指定します。
実際には、現在直下のスクリプトの相対パス一覧が自動的に補完されます。
なので、その中から選ぶだけです。
それ以外の値を入力すると、検査に違反してエラーになります。
.INPUTS
パイプラインでの入力には対応していません。
.OUTPUTS
スクリプトの実行結果を返します。
.EXAMPLE
PS C:\Sample> Invoke-PsScript .\sample.ps1
.EXAMPLE
PS C:\Sample> .. .\sample.ps1
.EXAMPLE
PS C:\Sample> Invoke-Script .\sample.ps1
#>
[Alias("..", "Invoke-Script")]
[CmdletBinding()]
param(
[ValidateSet([PsScriptRelativePaths])]
[string] $Path
)
. $Path
}
代替案
PowerShell5なら、以下の代替案を使います。
フォーマット
function 許容値配列生成の関数名{
処理
}
function 関数名{
param(
[ArgumentCompleter({許容値配列生成の関数名})]
[ValidateScript({$_ -in $(許容値配列生成の関数名)})]
[型名] $変数名
)
}
コード例
function Get-PsScriptRelativePaths{
[OutputType([String[]])]
[CmdletBinding()]
Param()
return $(Resolve-Path -Relative -LiteralPath @(Get-ChildItem -Path "./" -Filter "*.ps1").FullName)
}
function Invoke-PsScript{
<#
.SYNOPSIS
PowerShellScriptを実行します
.DESCRIPTION
この関数を使うと、以下のメリットがあります:
1. PowerShellのスクリプトパスのみ補完してくれる
一方備え付けのドット演算子には以下のデメリットがあります:
1. PowerShellのスクリプト以外のパスを補完してしまう
関数別名は、以下のようになっています:
1. `..` -> ドット演算子との類似性を保つため
2. `Invoke-Script` -> PowerShell内でのScriptは基本PowerShellスクリプトのため
.PARAMETER Path
人間にとって扱いやすい相対パスで、
現在地直下のスクリプトを指定します。
実際には、現在直下のスクリプトの相対パス一覧が自動的に補完されます。
なので、その中から選ぶだけです。
それ以外の値を入力すると、検査に違反してエラーになります。
.INPUTS
パイプラインでの入力には対応していません。
.OUTPUTS
スクリプトの実行結果を返します。
.EXAMPLE
PS C:\Sample> Invoke-PsScript .\sample.ps1
.EXAMPLE
PS C:\Sample> .. .\sample.ps1
.EXAMPLE
PS C:\Sample> Invoke-Script .\sample.ps1
#>
[Alias("..", "Invoke-Script")]
[CmdletBinding()]
param(
[ArgumentCompleter({Get-PsScriptRelativePaths})]
[ValidateScript({$_ -in $(Get-PsScriptRelativePaths)})]
[String] $Path
)
. $Path
}
実行結果
両者ともに実用上の効果は同じなので、
1つの動画で紹介します。
補足
DynamicParam
を使う方法もありますが、特別な理由がない限り使わないでください。
理由は、現時点で、この記事で紹介した方法に比べて、
同等の効果を得るためのコーディング労力が大きすぎるためです。
DynamicParam
が推奨されていたのは、
この記事で述べた方法が登場する以前だと考えてください。
参照資料
今回の記事の参考になった記事
この記事を書くにあたり大変参考にした記事です。
DynamicParamを使った古い方法の記事
下の記事の内容は、
当該記事が書かれた当初は最適の
素晴らしい解決策でしたが、
現在では非推奨です。
参考としてお読みください。