調べてみたら PowerShell の パラメータ指定が奥深かったのでメモ。
公式ドキュメント を読む限り、 ArgumentCompleter
などほかにも相当奥が深そうですが、まずは ParameterSet
についてまとめておきます。
環境:
Name Value
---- -----
PSVersion 7.0.3
PSEdition Core
GitCommitId 7.0.3
OS Microsoft Windows 10.0.18362
Platform Win32NT
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
WSManStackVersion 3.0
ParameterSet とは
The ParameterSetName argument specifies the parameter set to which a parameter belongs.
渡されたパラメータが属するセットを指定できるよ、とのことです。
具体例1
function Test-ParamSet {
[CmdletBinding(DefaultParameterSetName="A")]
param (
[Parameter(ParameterSetName="A")]
[Switch]$a,
[Parameter(ParameterSetName="B")]
[Switch]$b,
[Parameter(ParameterSetName="C")]
[Switch]$c,
[Parameter(ParameterSetName="D")]
[Switch]$d,
[Parameter(ParameterSetName="E")]
[Switch]$e,
[switch]$nonSet
)
Write-Host $PSCmdlet.ParameterSetName
}
Test-ParamSet -
とハイフンまで入力して Tab を押すと、 -a
~ -e
および -nonSet
のほかに共通パラメータの -Verbose
などが補完されます。
そして重要な点として、たとえば Test-ParamSet -a
とパラメータ -a
を指定すると、 -b
~ -e
は Tab を押しても 補完されなくなります 。
無理やり指定するとエラーになります。
> Test-ParamSet -a -b
Test-ParamSet: Parameter set cannot be resolved using the specified named parameters. One or more parameters issued cannot be used together or an insufficient number of parameters were provided.
これは、パラメータ -a
を指定した時点で「パラメータセットの A
を使用している」と解釈され、残りの B
などのセットが補完の候補から外されたということです(対して、 -nonSet
はセットを指定していないので常に補完の対象)。
具体例2
各パラメータセットのメンバーを増やしてみます。
function Test-ParamSet2 {
[CmdletBinding(DefaultParameterSetName="A")]
param (
[Parameter(ParameterSetName="A")]
[Switch]$a1,
[Parameter(ParameterSetName="A")]
[Switch]$a2,
[Parameter(ParameterSetName="B")]
[Switch]$b1,
[Parameter(ParameterSetName="B")]
[Switch]$b2,
[Parameter(ParameterSetName="B")]
[Switch]$b3,
[Parameter(ParameterSetName="C")]
[Switch]$c1,
[Parameter(ParameterSetName="C")]
[Switch]$c2,
[Parameter(ParameterSetName="E")]
[Switch]$e
)
Write-Host $PSCmdlet.ParameterSetName
}
Test-ParamSet2 -a1
とするとセット A
が適用され、 -a2
には Tab 補完が効きますが、 -b1
などは候補に挙がらなくなります。
つまり、「●●の処理をしたいときはセット A
」といったように、用途別にパラメータのセットを指定できるわけです。
具体例3
公式のサンプル からパラメータ指定部分を抜き出してみます。
function Measure-Lines {
[CmdletBinding(DefaultParameterSetName = 'Path')]
param (
[Parameter(Mandatory = $true,
ParameterSetName = 'Path',
HelpMessage = 'Enter one or more filenames',
Position = 0)]
[Parameter(Mandatory = $true,
ParameterSetName = 'PathAll',
Position = 0)]
[string[]]$Path,
[Parameter(Mandatory = $true, ParameterSetName = 'LiteralPathAll')]
[Parameter(Mandatory = $true,
ParameterSetName = 'LiteralPath',
HelpMessage = 'Enter a single filename',
ValueFromPipeline = $true)]
[string]$LiteralPath,
[Parameter(ParameterSetName = 'Path')]
[Parameter(ParameterSetName = 'LiteralPath')]
[switch]$Lines,
[Parameter(ParameterSetName = 'Path')]
[Parameter(ParameterSetName = 'LiteralPath')]
[switch]$Words,
[Parameter(ParameterSetName = 'Path')]
[Parameter(ParameterSetName = 'LiteralPath')]
[switch]$Characters,
[Parameter(Mandatory = $true, ParameterSetName = 'PathAll')]
[Parameter(Mandatory = $true, ParameterSetName = 'LiteralPathAll')]
[switch]$All,
[Parameter(ParameterSetName = 'Path')]
[Parameter(ParameterSetName = 'PathAll')]
[switch]$Recurse
)
}
このようにパラメータ1つに複数のパラメータセットを割り当てることもできるそうです。詳細に書かれているので複雑になりましたが、ここでは Path
PathAll
LiteralPath
LiteralPathAll
の4種類のパラメータセットが指定されています。
少し複雑なので表にしました(セット名を行ごとに整理)。
-Path | -LiteralPath | -Lines | -Words | -Characters | -All | -Recurse | |
---|---|---|---|---|---|---|---|
Path | ○ | ○ | ○ | ○ | ○ | ||
PathAll | ○ | ○ | ○ | ||||
LiteralPath | ○ | ○ | ○ | ○ | |||
LiteralPathAll | ○ | ○ |
Measure-Lines -Path \.hoge.txt
のようにして -Path
を指定すると、セットは Path
もしくは PathAll
に絞り込まれ、 -LiteralPath
と -LiteralPathAll
は補完されなくなります。
同様に、 -Lines
を指定すると、その時点で PathAll
と LiteralPathAll
のセットは使われないことが確定するので -All
は補完されなくなります。
極端なところでは -All
を指定すると、 -Path
と -LiteralPath
と -Recurse
しか補完されなくなります。
実際に補完動作を見ながらだとわかりやすいと思います。
実践
google 検索するコマンドレットを作ってみました。
-global
でグローバル検索、 -image
で画像検索といったように、スイッチパラメータで検索モードを切り替えられます。 ParameterSetName
のおかげで複数のモードを同時に指定してしまうというミスが減っています。
function Invoke-GoogleSearch {
[CmdletBinding(DefaultParameterSetName="global")]
param (
[Parameter(ParameterSetName="global")]
[Switch]$global,
[Parameter(ParameterSetName="image")]
[Switch]$image,
[Parameter(ParameterSetName="map")]
[Switch]$map,
[Parameter(ParameterSetName="scholar")]
[Switch]$scholar,
[switch]$strict,
[Parameter(ValueFromRemainingArguments)]
[string[]]$s
)
$keyword = ($strict)?
($s | ForEach-Object{'"{0}"' -f $_}) -join " " :
$s -join " "
$url = switch ($PsCmdlet.ParameterSetName) {
"global" {"http://www.google.co.jp/search?q={0}"; break}
"image" {"https://www.google.com/search?tbm=isch&q={0}"; break}
"map" {"https://www.google.co.jp/maps/search/{0}"; break}
"scholar" {"https://scholar.google.co.jp/scholar?q={0}"; break}
}
Start-Process ([string]$url -f [System.Web.HttpUtility]::UrlEncode($keyword))
}
備考: コマンドライン引数を受け取るには自動変数 $args
を使うことが多いですが、パラメータを色々設定しているとこの方法は使えないようです。同様の動きを実現させるためには ValueFromRemainingArguments
を指定する必要があるようです。