LoginSignup
1
3

More than 3 years have passed since last update.

【PowerShell】ParameterSet を活用する

Posted at

調べてみたら 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 を指定すると、その時点で PathAllLiteralPathAll のセットは使われないことが確定するので -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 を指定する必要があるようです。

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3