HOME > PowerShell > Tips > 高度な関数

高度な関数


作成日 2017年09月12日
更新日 2018年05月24日
前提としてPowerShellの関数についての知識が必要です。関数については下記のマイクロソフトのサイトを参照してください。
【外部リンク】 Windows PowerShell 5.0 > Microsoft.PowerShell.Core モジュール > コア About トピック > about_Functions

[CmdletBinding()] を使用して高度な関数にします。
パラメーターに属性を指定できます。
-Debug、-ErrorAction、-WarningAction、-Write-Debug、-InformationAction、-ErrorVariableなどの共通パラメーターが使用できます。
関数内で自動変数の$PSCmdletを使用して、メソッドとプロパティにアクセスします。
スクリプトブロックの Beginは始めに1回実行します。Processはパイプラインの繰り返し処理をします。Endは最後に1回実行します。
Write-Outputコマンドレットの出力は関数の戻り値になりますが、Write-Hostは戻り値になりません。

  1. 高度な関数の宣言の例
  2. CmdletBinding属性
  3. パラメーター属性
  4. パラメーターと変数の検証属性
  5. パラメーターを必須にする(Mandatory)
  6. パラメーターが必須なのに省略した場合に表示するヘルプメッセージ(HelpMessage)
  7. 文字列が空でも許可する(AllowEmptyString)
  8. パラメーターの数を検査する(ValidateCount)
  9. 文字数を検査する(ValidateLength)
  10. 範囲を検査する(ValidateRange)
  11. 正規表現で検査する(ValidatePattern)
  12. スクリプトで検査する(ValidateScript)
  13. 複数の項目から1つを選択する(ValidateSet)
  14. 指定すると$trueになるスイッチ パラメーター(Switch)
  15. パラメーターに別の名前を付ける(Alias)
  16. パラメーターの型や数が違うオーバーロード(ParameterSetName)
  17. パラメーターの-First、-Skip、-IncludeTotalCountを使用する(SupportsPaging)
  18. パラメーターの-Confirmと-WhatIfを使用する(SupportsShouldProcess)
  19. 確認プロンプトを表示する(SupportsShouldProcess、ConfirmImpact)
  20. 無条件で確認プロンプトを表示する(ShouldContinue)
  21. パイプラインの入力値をパラメーターとする(ValueFromPipeline)
  22. パラメーターと同じ名前のパイプラインのプロパティを使う(ValueFromPipelineByPropertyName)
  23. 余ったパラメーターを使う(ValueFromRemainingArguments)
  24. ワイルドカードを使う(SupportsWildcards)
  25. 関数が返すオブジェクトの型(OutputType)
  26. 動的パラメーター(DynamicParam)

高度な関数の宣言の例

下記のソースをコピーしても動作しません。
function Test-Sample
{
    [CmdletBinding(ConfirmImpact="High",
                  DefaultParameterSetName="パラメーターセット名1", 
                  HelpUri="http://suyamasoft.blue.coocan.jp/",
                  PositionalBinding=$false,
                  RemotingCapability="SupportedByCommand",
                  SupportsPaging=$true,
                  SupportsShouldProcess=$true
                  )]
    [OutputType([型])]
	
    Param
    (
        # パラメーター 1 のヘルプの説明
        [Parameter(HelpMessage="ヘルプメッセージ",
                   Mandatory=$true, 
                   ParameterSetName="パラメーターセット名1",
                   Position=0,
                   ValueFromPipeline=$true,
                   ValueFromPipelineByPropertyName=$true, 
                   ValueFromRemainingArguments=$false
                  )]
        [AllowEmptyCollection()]
        [AllowEmptyString()]
        [Alias("別名1", "別名2")] 
        [String[]]
        $Param1,

        # パラメーター 2 のヘルプの説明
        [Parameter(ParameterSetName="パラメーターセット名1")]
        [ValidateScript({$true})]
        [ValidateRange(最小値, 最大値)]
        [int]
        $Param2,

        # パラメーター 3 のヘルプの説明
        [Parameter(ParameterSetName="パラメーターセット名2")]
        [AllowNull()]
        [ValidateNotNull()]
        [ValidateNotNullOrEmpty()]
        [ValidateSet("値1", "値2", "値3")]
        [ValidatePattern("正規表現のパターン")]
        [ValidateLength(最小の文字数, 最大の文字数)]
        [String]
        $Param3,

        # パラメーター 4 のヘルプの説明
        [Parameter(ParameterSetName="パラメーターセット名2")]
        [Switch]
        $Param4,

        # パラメーター 5 のヘルプの説明
        [Parameter(ParameterSetName="パラメーターセット名2", Mandatory=$false)]
        [SupportsWildcards()]
        [ValidateCount(最小数, 最大数)]
        [String]
        $Param5="*"
    )
}

このページの上

CmdletBinding属性

パラメーター説明
ConfirmImpactConfirmImpactがユーザー設定変数の$ConfirmPreferenceの値以上か、-Confirmパラメーターを指定した場合に確認プロンプトを表示します。(High、Medium、Low、None)
確認プロンプトを常に表示するには、Highにします。ConfirmImpact="High"(ただし $ConfirmPreference="None" の場合は表示しません)
DefaultParameterSetNameパラメーター名を省略して実行した時の、既定のパラメーターセット名です。(Parameter属性のParameterSetNameの値)
HelpURIオンラインヘルプのインターネットアドレスです。 (XMLヘルプファイルのRelatedLinksセクションや、コメントベースヘルプの.Linkの方が優先します)
PositionalBinding$trueはパラメーター名を省略して実行すると、位置指定パラメーターにします。(既定値は$true)
RemotingCapabilityリモート実行時の挙動を決定する値です。(None、OwnedByCommand、PowerShell、SupportedByCommand)既定はPowerShellです。
SupportsPaging$trueは-First、-Skip、-IncludeTotalCountパラメーターが使えます。(関数内で$PSCmdlet.PagingParameters.Firstなどを使用します)
SupportsShouldProcess$trueは-Confirm、-WhatIfパラメーターが使えます。 if ($PSCmdlet.ShouldProcess("対象","操作")) {"ここに実行する処理を記載します。"}
SupportsTransactionsは、高度な関数ではサポートされません。
【外部リンク】マイクロソフトのabout_Functions_CmdletBindingAttributeを参照してください。
【外部リンク】マイクロソフトのWindows PowerShell: スクリプトでコマンドレットを記述するを参照してください。

このページの上

パラメーター属性

Parameter[(Mandatory=$true, Position=0, ParameterSetName="ParameterSetName1", ValueFromPipeline=$true,
 ValueFromPipelineByPropertyName=$true, ValueFromRemainingArguments=$true, HelpMessage="Help Message for Parameter")]
Parameter属性の引数説明
Mandatory$trueはパラメーターが必須です。
Position0始まりのパラメーターの位置です。(CmdletBinding属性のPositionalBindingが$trueの場合はパラメーター名を省略できます)
ParameterSetNameパラメーターセット名です。(オーバーロードのためのグループ名)
ValueFromPipeline$trueはパイプラインから入力のオプジェクトをパラメーターにします。
ValueFromPipelineByPropertyName$trueはパラメーターと同じパイプラインから入力のオプジェクトのプロパティ名を含めます。
ValueFromRemainingArguments$trueは余ったパラメーターを使用します。
HelpMessage入力必須のパラメーターを指定せず実行すると、(ヘルプを表示するには、「!?」と入力してください。)と表示されます。
!?を入力すると表示されるヘルプメッセージです。

Parameter以外の属性説明
[Alias("別名1","別名2","別名3"...)]別名を指定します。(複数可)
[SupportsWildcards()]パラメーターの値にワイルドカードを使えます。
$psCmdlet.SessionState.Path.GetResolvedProviderPathFromPSPath()
$psCmdlet.SessionState.Path.GetUnresolvedProviderPathFromPSPath()
[Switch]スイッチ パラメーターです。パラメーターを指定すると$true、指定しないと$falseです。
【外部リンク】マイクロソフトのabout_Functions_Advanced_Parametersを参照してください。


このページの上

パラメーターと変数の検証属性

検証属性はパラメーターだけでなく、スクリプト内の変数にも使用できるようになりました。
検証属性説明
AllowNull()$nullを許可します。
AllowEmptyString()空の文字列を許可します。改行のみの行があるテキストファイルは必要です。
AllowEmptyCollection()空のコレクションを許可します。
ValidateCount(最小数, 最大数)パラメーター数の最小数と最大数です。
ValidateLength(最小文字数, 最大文字数)変数の値に含まれる最小文字数と最大文字数です。
ValidatePattern("パターン")正規表現のパターンです。
ValidateRange(最小値, 最大値)最小値から最大値の範囲の数値です。
ValidateScript({スクリプトの条件})]スクリプトで $falseを返すとエラーです。
ValidateSet("値1","値2","値3"...)複数の項目から1つを選択します。文字列でなく、数値でも良いです。
ValidateNotNull()null ($null) にできません。
ValidateNotNullOrEmpty()null ($null) および空の文字列 ("") にできません。

このページの上

パラメーターを必須にする(Mandatory)

Parameter属性のMandatory引数を$trueにします。
[Parameter(Mandatory=$true)]
function Test-Sample
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$true)]
        [String]
        $Param1
    )
	
    Write-Output $Param1
}
ファイル名は「Sample.ps1」で保存しました。これ以降のサンプルのファイル名は「Sample.ps1」を使用します。

ファイル名の前に半角のドット記号とスペースを連結するドットソースの形式の「. .\Sample.ps1」コマンドを実行して、関数をグローバルスコープにします。
これ以降のサンプルは関数を実行する前にドットソースの形式でスクリプトを読み込みます。
PS > . .\Sample.ps1

パラメーターなしで「Test-Sample」関数を実行すると、「次のパラメーターに値を指定してください:」と表示されます。
コマンド パイプライン位置 1 のコマンドレット Test-Sample
次のパラメーターに値を指定してください:

このページの上

パラメーターが必須なのに省略した場合に表示するヘルプメッセージ(HelpMessage)

Parameter属性のHelpMessage引数の値にヘルプメッセージを指定します。
[Parameter(HelpMessage="ヘルプメッセージ")]
function Test-Sample
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$true, HelpMessage="お名前を入力してください。")]
        [String]
        $Param1
    )

    Write-Output $Param1
}

パラメーターなしで「Test-Sample」関数を実行すると ヘルプを表示するには、「!?」と入力してください。) と表示されます。
「!?」を入力してENTERキーを押すと、HelpMessageに指定した「お名前を入力してください。」が表示されます。
PS C:\bin\PowerShell\Sample> Test-Sample
コマンド パイプライン位置 1 のコマンドレット Test-Sample
次のパラメーターに値を指定してください:
(ヘルプを表示するには、「!?」と入力してください。)
Param1: !?
お名前を入力してください。
Param1: 

このページの上

文字列が空でも許可する(AllowEmptyString)

AllowEmptyString属性は文字列が空でも許可します。既定でstring型の配列は、指定しないと空はエラーになります。空行を含む場合は指定してください。
[AllowEmptyString()]
function Test-Sample
{
    [CmdletBinding()]
    [Outputtype([Object[]])]

    Param(
        [Parameter(Mandatory=$true, ValueFromPipeline=$true, HelpMessage="複数行の文字列")]
        [AllowEmptyString()]
        [String[]]$Lines
    )

    process{
        foreach ($s in $Lines){
            Write-Output $s    
        }    
    }
}

このページの上

パラメーターの数を検査する(ValidateCount)

ValidateCount検証属性にパラメーターの数の最小数と最大数を指定します。
[ValidateCount(最小数, 最大数)]
function Test-Sample
{
    [CmdletBinding()] 
    Param (
        [ValidateCount(2,3)] 
        [String[]]$Parmam1
    )
    
    Write-Output $Parmam1
}

パラメーターの数が範囲外はエラーになります。
PS > Test-Sample -Parmam1 ("One" ,"Two")
One Two

PS > Test-Sample -Parmam1 ("a", "b", "c", "d")
Test-Sample : パラメーター 'Parmam1 の引数を確認できません。指定された引数の数 (4) が、引数の最大許容数 (3) を超えています。3 未満の数の引数を指定して、コマンドを再度実行してください。
発生場所 行:1 文字:22
+ Test-Sample -Parmam1 ("a", "b", "c", "d")
+                      ~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidData: (:) [Test-Sample]、ParentContainsErrorRecordException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Test-Sample

このページの上

文字数を検査する(ValidateLength)

ValidateLength検証属性にパラメーターの文字数の最小数と最大数を指定します。
[ValidateLength(最小数, 最大数)]
function Test-Sample
{
    [CmdletBinding()] 
    Param ( 
        [ValidateLength(2,3)] 
        [String]$Parmam1
    )
    
    Write-Output $Parmam1
}

パラメーターの文字数が範囲外はエラーになります。
PS > Test-Sample -Parmam1 ab
ab

PS > Test-Sample -Parmam1 a
Test-Sample : パラメーター 'Parmam1 の引数を確認できません。引数の文字の長さ (1) が短すぎます。"2" 以上の長さの引数を指定して、コマンドを再度実行してください。
発生場所 行:1 文字:22
+ Test-Sample -Parmam1 a
+                      ~
    + CategoryInfo          : InvalidData: (:) [Test-Sample]、ParentContainsErrorRecordException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Test-Sample

このページの上

範囲を検査する(ValidateRange)

ValidateRange検証属性にパラメーターの最小値と最大値を指定します。
[ValidateRange(最小値, 最大値)]
function Test-Sample
{
    [CmdletBinding()] 
    Param ( 
        [ValidateRange(1,10)]
        [Int]$Parmam1
    )
    
    Write-Output $Parmam1
}

パラメーターの数値の範囲外はエラーになります。
PS > Test-Sample -Parmam1 5
5

PS > Test-Sample -Parmam1 11
Test-Sample : パラメーター 'Parmam1 の引数を確認できません。引数 11 が、最大許容範囲 10 を超えています。10 以下の引数を指定して、コマンドを再度実行してください。
発生場所 行:1 文字:22
+ Test-Sample -Parmam1 11
+                      ~~
    + CategoryInfo          : InvalidData: (:) [Test-Sample]、ParentContainsErrorRecordException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Test-Sample

このページの上

正規表現で検査する(ValidatePattern)

ValidatePattern検証属性にパラメーターの正規表現のパターンを指定します。
[ValidatePattern("正規表現のパターン")]
正規表現はマイクロソフトのサイトの「about_Regular_Expressions」や「正規表現言語 - クイック リファレンス」を参照してください。
function Test-Sample
{
    [CmdletBinding()] 
    Param ( 
        [ValidatePattern("[a-zA-Z][0-9][0-9]")]
        [String]$Parmam1
    )
    
    Write-Output $Parmam1
}

正規表現のパターンに一致しないパラメーターの値はエラーになります。
PS > Test-Sample -Parmam1 G38
G38

PS > Test-Sample -Parmam1 F1
Test-Sample : パラメーター 'Parmam1 の引数を確認できません。引数 "F1" が "[a-zA-Z][0-9][0-9]" パターンに一致しません。"[a-zA-Z][0-9][0-9]" に一致する引数を指定して、コマンドを再度実行してください。
発生場所 行:1 文字:22
+ Test-Sample -Parmam1 F1
+                      ~~
    + CategoryInfo          : InvalidData: (:) [Test-Sample]、ParentContainsErrorRecordException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Test-Sample

このページの上

スクリプトで検査する(ValidateScript)

ValidateScript検証属性にパラメーターを検査するスクリプトを指定します。
スクリプトの中でパラメーターの変数は、$_を使用します。
[ValidateScript({スクリプト})]
function Test-Sample
{
    [CmdletBinding()] 
    Param ( 
        [ValidateScript({$_ -ge (((Get-Date).Date).AddDays(2))})]
        [DateTime]$Parmam1=((Get-Date).Date).AddDays(2)
    )
    
    Write-Output $Parmam1.ToString("yyyy/MM/dd")
}

パラメーターのスクリプトの戻り値が$falseはエラーになります。(実行したのは2017年9月7日で、2日以降の日付はOKです)
PS > Get-Date -DisplayHint Date
2017年9月7日

PS > Test-Sample -Parmam1 2017/09/09
2017/09/09

PS > Test-Sample -Parmam1 2017/09/08
Test-Sample : パラメーター 'Parmam1 の引数を確認できません。値 "17/9/8 0:00:00" が指定された引数の "$_ -ge (((Get-Date).Date).AddDays(2))" 検証スクリプトで結果として True が返されませんでした。検証スクリプトが失敗した理由を確認し、コマンドを再度実行してください。
発生場所 行:1 文字:22
+ Test-Sample -Parmam1 2017/09/08
+                      ~~~~~~~~~~
    + CategoryInfo          : InvalidData: (:) [Test-Sample]、ParentContainsErrorRecordException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Test-Sample

エラーの場合にスクリプトブロックのソースが表示されますが、メッセージを表示するにはthrowしてください。
function Test-Sample
{
    [CmdletBinding()] 
    Param ( 
        [ValidateScript({if ($_ -ge ((Get-Date).Date).AddDays(2) ){
                $true
            } else {
                throw("2日後以降の日付を指定してください!")
            }
        })]
        [DateTime]$Parmam1=((Get-Date).Date).AddDays(2)
    )
    
    Write-Output $Parmam1.ToString("yyyy/MM/dd")
}

PS > Test-Sample -Parmam1 2017/09/08
Test-Sample : パラメーター 'Parmam1 の引数を確認できません。2日後以降の日付を指定してください!
発生場所 行:1 文字:13
+ Test-Sample 2017/09/08
+             ~~~~~~~~~~
    + CategoryInfo          : InvalidData: (:) [Test-Sample]、ParentContainsErrorRecordException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Test-Sample

このページの上

複数の項目から1つを選択する(ValidateSet)

ValidateSet検証属性に項目の一覧を指定します。
[ValidateSet("項目1", "項目2", "項目3"...)]
一覧にないパラメーターで実行するとエラーになります。
function Test-Sample
{
    [CmdletBinding()] 
    Param ( 
        [parameter(Mandatory=$true)]
        [ValidateSet("Red", "Green", "Blue")]
        [String]$Color
    )
    
    Write-Output $Color
}

「Test-Sample -Color 」の後でCTRLキーを押しながらスペースキーを押すと一覧が表示され選択できます。(下記はWindows PowerShell ISE)
ポップアップメニュー
PS > Test-Sample -Color Blue
Blue

このページの上

指定すると$trueになるスイッチ パラメーター(Switch)

[Switch]
function Test-Sample
{
    [CmdletBinding()]
    Param
    (
        [Switch]
        $CaseSensitive
    )

    if ($CaseSensitive -eq $true){
        Write-Output "`$CaseSensitive = True"
    }else{
        Write-Output "`$CaseSensitive = False"
    }
}

[Switch]はパラメーターを指定して実行すると$trueになり、指定しないと$falseになります。
PS > Test-Sample -CaseSensitive
$CaseSensitive = True

PS > Test-Sample
$CaseSensitive = False

明示的に指定するには、パラメーター名の後に半角のコロン(:)を付け、$trueか$falseを続けます。
PS > Test-Sample -CaseSensitive:$true
$CaseSensitive = True

PS > Test-Sample -CaseSensitive:$false
$CaseSensitive = False

このページの上

パラメーターに別の名前を付ける(Alias)

Alias属性の引数に別の名前を指定します。(複数可)
[Alias("別名1","別名2","別名3"...)]
function Test-Sample
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$true)]
        [Alias("Count","Cnt")]
        [Int]
        $Length
    )

    Write-Output $Length
}

パラメーターのエイリアスはCTRLキーを押しながらスペースキー押しても表示されません。
しかし最初の文字を入力後にTABキーを押すと補完されます。
PS > Test-Sample -Length 1
1

PS > Test-Sample -Count 2
2

PS > Test-Sample -Cnt 3
3

このページの上

パラメーターの型や数が違うオーバーロード(ParameterSetName)

Parameter属性の引数のParameterSetNameに名前を付けて分類します。この例のパラメーターセット名は「Set1」と「Set2」です。
[Parameter(ParameterSetName="パラメーターセット名")]
Parameter属性の引数のPositionは0始まりのパラメーターの位置です。
実行したパラメーターセット名の区別は、$PSCmdlet.ParameterSetNameを使用します。
パラメーター名を省略した場合の既定のパラメーターセット名はCmdletBindingのDefaultParameterSetNameに指定します。下記の例はSet1です。
function Test-Sample
{
    [CmdletBinding(DefaultParameterSetName="Set1")]
    Param
    (
        [Parameter(Mandatory=$true, Position=0, ParameterSetName="Set1")]
        [Int]
        $Code,

        [Parameter(Mandatory=$true, Position=0, ParameterSetName="Set2")]
        [String]
        $Name,

        [Parameter(Mandatory=$true, Position=1, ParameterSetName="Set2")]
        [DateTime]
        $Date
    )

    if ($PSCmdlet.ParameterSetName.Equals("Set1")){
        Write-Output "`$Code:$Code"
    }elseif ($PSCmdlet.ParameterSetName.Equals("Set2")){
        Write-Output "`$Name:$Name"
        Write-Output ("`$Date:" + $Date.ToString("yyyy/MM/dd"))
    }
}

2種類の方法で実行できます。
PS > Test-Sample -Code 123
$Code:123

PS > Test-Sample -Name yamada -Date 2017/09/12
$Name:yamada
$Date: 2017/09/12

パラメーター名を省略して実行すると、CmdletBinding属性のDefaultParameterSetNameの値がSet1なので$Codeになります。
PS > Test-Sample 789
$Code:789

このページの上

パラメーターの-First、-Skip、-IncludeTotalCountを使用する(SupportsPaging)

CmdletBinding属性の引数のSupportsPagingを$trueにします。
[CmdletBinding(SupportsPaging=$true)]
関数内で$PSCmdlet.PagingParametersオブジェクトのFirst、Skip、IncludeTotalCountプロパティを使用します。
function Test-Sample
{
    [CmdletBinding(SupportsPaging=$true)]
    Param()
    Write-Output ('$PSCmdlet.PagingParameters.First = ' + $PSCmdlet.PagingParameters.First)
    Write-Output ('$PSCmdlet.PagingParameters.Skip = ' + $PSCmdlet.PagingParameters.Skip)
    Write-Output ('$PSCmdlet.PagingParameters.IncludeTotalCount = ' + $PSCmdlet.PagingParameters.IncludeTotalCount)
}

PS > Test-Sample -First 1 -Skip 2 -IncludeTotalCount
$PSCmdlet.PagingParameters.First = 1
$PSCmdlet.PagingParameters.Skip = 2
$PSCmdlet.PagingParameters.IncludeTotalCount = True
-Firstを省略すると[uint64]::MaxValueと同じ値の「18446744073709551615」になります。

下記は-First、-Skip、-IncludeTotalCountを自分で実装しました。
function Test-Sample
{
    [CmdletBinding()]
    Param(
        [uint64]$First=[uint64]::MaxValue,

        [uint64]
        $Skip=0,

        [Switch]$IncludeTotalCount
    )

    Write-Output "`$First = $First"
    Write-Output "`$Skip = $Skip"
    Write-Output "`$IncludeTotalCount = $IncludeTotalCount"
}

このページの上

パラメーターの-Confirmと-WhatIfを使用する(SupportsShouldProcess)

CmdletBinding属性の引数のSupportsShouldProcessを$trueにします。
[CmdletBinding(SupportsShouldProcess=$true)]
パラメーターの -WhatIfを指定したかは、($PSBoundParameters["WhatIf"] -ne $null)で判断します。
ユーザー設定変数の$WhatIfPreferenceが$trueの場合は、-WhatIfと同じ処理をすべきです。
メッセージを$PSCmdlet.ShouldProcess("対象", "操作")で表示します。

確認プロンプトを表示しない場合はCmdletBinding属性のConfirmImpactの値を、"None"にしてください。
(Noneを指定してもパラメーターの-Confirmは入力できますが確認プロンプトは表示しません)
-WhatIfと -Confirmの両方を指定して実行すると、$PSCmdlet.ShouldProcessは -WhatIfを実行します。
function Test-Sample
{
    [CmdletBinding(SupportsShouldProcess=$true)]
    Param([String]$Param1)

    if (($WhatIfPreference -eq $true) -or ($PSBoundParameters["WhatIf"] -ne $null)) {
        # -WhatIf
        [void]$PSCmdlet.ShouldProcess($Param1, "サンプルのテスト")
        return
    } 

    if ($PSCmdlet.ShouldProcess($Param1, "サンプルのテスト") -eq $false) {
        Write-Host "「いいえ」か、あるいは「すべて無視」ボタンを実行しました。"
        return
    }    

    Write-Output "主処理を実行します。"
}

PS > Test-Sample TestFile.txt -WhatIf
WhatIf: 対象 "TestFile.txt" に対して操作 "サンプルのテスト" を実行しています。

PS > Test-Sample TestFile.txt -Confirm
主処理を実行します。
上記は、確認メッセージに[はい]ボタンを押しました。(下記はWindows PowerShell ISE)
確認メッセージ

このページの上

確認プロンプトを表示する(SupportsShouldProcess、ConfirmImpact)

CmdletBinding属性の引数のSupportsShouldProcessを$trueに、ConfirmImpactの値をHigh、Medium、Lowのどれかにします。
[CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="High")]
関数のパラメーターの -Confirmを指定すると確認プロンプトを要求します。
$ConfirmImpactの値がNoneの場合は、パラメーターの -Confirmを指定しても表示しません。

-Confirmを指定しなくてもCmdletBinding属性の引数のConfirmImpactの値が、ユーザー設定変数の$ConfirmPreferenceの値以上で表示します。(High、Medium、Low
コマンドレットの実行時に常に確認を要求するには、「ConfirmImpact="High"」にします。($ConfirmPreference="None"の場合は表示しません)

押したボタンは$PSCmdlet.ShouldProcess("対象", "操作")の戻り値で判断します。
[はい]か[すべて続行]ボタンは$trueで、[いいえ]か[すべて無視]ボタンは$falseです。
【注意】確認プロンプトが表示されない場合は$trueです。

-WhatIfと -Confirmの両方を指定して実行すると、$PSCmdlet.ShouldProcessは -WhatIfを実行します。
確認プロンプトは$Host.UIのPromptForChoiceメソッドを使用する方法もあります。
function Test-Sample
{
    [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="High")]
    Param([String]$Param1)

    if (($WhatIfPreference -eq $true) -or ($PSBoundParameters["WhatIf"] -ne $null)) {
        # -WhatIf
        [void]$PSCmdlet.ShouldProcess($Param1, "サンプルのテスト")
        return
    } 

    if ($PSCmdlet.ShouldProcess($Param1)) {
        # 【注意】確認プロンプトを表示しない場合はこの処理を実行します。
        if ($ConfirmPreference -eq "None"){
            # パラメーターの-Confirmを指定しないで、かつ $ConfirmPreference = "None" の場合
            Write-Host "`$ConfirmPreference = $ConfirmPreference"
        } else {
            Write-Host "[はい]か、あるいは[すべて続行]ボタンを押しました。"
        }
    } else {
        # [中断]ボタンを押した場合は終了してます。
        Write-Host "[いいえ]か、あるいは[すべて無視]ボタンを押しました。"
        return
    }    

    Write-Output "主処理を実行します。"
}

メッセージの操作は省略したので関数名を表示します。
確認ダイアログ
$PSCmdlet.ShouldProcessの戻り値は[はい]、[すべて続行]ボタンが$trueで、[いいえ]、[すべて無視]ボタンが$falseです。
[すべて続行]か[すべて無視]ボタンを押すと、次回の$PSCmdlet.ShouldProcessメソッドは実行しません。
「中断」ボタンは処理を途中で終了します。

パラメーターの -Confirmを指定しなくても、ConfirmImpact="High"の場合は確認メッセージを表示します。($ConfirmPreference="None"の場合は表示しません)
PS > Test-Sample TestFile.txt -Confirm
[はい]か、あるいは[すべて続行]ボタンを押しました。
主処理を実行します。

PS > Test-Sample TestFile.txt
[いいえ]か、あるいは[すべて無視]ボタンを押しました。

PS > $ConfirmPreference = "None"

PS > Test-Sample TestFile.txt
$ConfirmPreference = None
主処理を実行します。

PS > Test-Sample TestFile.txt -WhatIf
WhatIf: 対象 "TestFile.txt" に対して操作 "サンプルのテスト" を実行しています。

前の例では繰り返し処理をしませんでした。
[すべて続行]か[すべて無視]ボタンを押した後に、確認プロンプトが表示されなくなる事を確認しましょう。
次の例は確認プロンプトを表示してから行番号付きで表示します。
function Test-Sample
{
    [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="High")]
    [Outputtype([Object[]])]

    Param(
        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
        [AllowEmptyString()]
        [String[]]$Lines,
        
        [String]$NumberFormat="00000: "
    )

    begin{

        if (($WhatIfPreference -eq $true) -or ($PSBoundParameters["WhatIf"] -ne $null)) {
            # -WhatIf
            [void]$PSCmdlet.ShouldProcess($Lines, "行番号付きで表示")
            return
        } 

        $LineCount=0
    }

    process{
        foreach ($s in $Lines){
            $LineCount++
            
            if ($PSCmdlet.ShouldProcess($Lines, "行番号付きで表示")) {
                Write-Output ($LineCount.ToString($NumberFormat) + $s)
            }    
        }    
    }
    
    end{
        Write-Output ("LineCount = " + $LineCount.ToString())
    }
}

[すべて続行]か[すべて無視]ボタンを押すと確認プロンプトは表示されなくなります。
PS > Get-ChildItem -Path $PSHome\p* | Test-Sample
00002: C:\Windows\System32\WindowsPowerShell\v1.0\PowerShellCore.format.ps1xml
00005: C:\Windows\System32\WindowsPowerShell\v1.0\powershell_ise.exe.config
00006: C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1
00008: C:\Windows\System32\WindowsPowerShell\v1.0\pspluginwkr.dll
LineCount = 10
[いいえ]ボタンを押した次の行は表示されません。次の行があれば確認プロンプトを表示します。
[すべて無視]ボタンを押した以降の行は表示されません。[すべて無視]ボタンを押してもすぐに終了しません。例ではスクリプトブロックのprocessで行番号を数えてます。

このページの上

無条件で確認プロンプトを表示する(ShouldContinue)

$PSCmdletのShouldContinueメソッドで確認プロンプトを表示します。
CmdletBinding属性のSupportsShouldProcessが$falseで、-Confirmパラメーターなしでも表示します。
ShouldContinueは2度目でなく、1度目でからも実行します。
【外部リンク】マイクロソフトのCmdlet::ShouldContinue Methodを参照しました。

$PSCmdlet.ShouldContinue("メッセージ", "ウィンドウのタイトル", [ref]$AllYes, [ref]$AllNo)
ShouldContinueの戻り値は[はい]、[すべて続行]ボタンが$trueで、[いいえ]、[すべて無視]ボタンが$falseです。
[すべて続行]ボタンを押した場合は、3番目の引数の$AllYesの値が$trueになります。
[すべて無視]ボタンを押した場合は、4番目の引数の$AllNoの値が$trueになります。
function Test-Sample
{
    [CmdletBinding()]
    [Outputtype([Object[]])]

    Param(
        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
        [AllowEmptyString()]
        [String[]]$Lines,
        
        [String]$NumberFormat="00000: "
    )

    begin{
        [int]$LineCount=0
        [bool]$AllYes=$false
        [bool]$AllNo=$false
    }

    process{
        foreach ($s in $Lines){
            $LineCount++

            if ($AllYes -eq $true){
                Write-Output ($LineCount.ToString($NumberFormat) + $s)
            } elseif ($AllNo -eq $false){
                if ($PSCmdlet.ShouldContinue("$Lines を行番号付きで表示しますか?", "確認", [ref]$AllYes, [ref]$AllNo)) {
                    Write-Output ($LineCount.ToString($NumberFormat) + $s)
                }  
            }  
        }    
    }
    
    end{
        Write-Output ("LineCount = " + $LineCount.ToString())
    }
}
最初に表示した時には[はい]ボタンを選択してますが、[いいえ]ボタンを選択するには下記のように3番目の引数に$trueを追加します。
if ($PSCmdlet.ShouldContinue("実行しますか?", "確認", $true, [ref]$AllYes, [ref]$AllNo))

このページの上

パイプラインの入力値をパラメーターとする(ValueFromPipeline)

Parameter属性のValueFromPipeline引数を$trueにします。
[Parameter(ValueFromPipeline=$true)]
最初の処理はbegin、繰り返しはprocess、最後はendのスクリプトブロックで処理します。
下記のサンプルは行番号を付けて表示します。
function Test-Sample
{
    [CmdletBinding()]
    [OutputType([Object[]])]
    Param(
        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
        [AllowEmptyString()]
        [string[]]$Lines,
        
        [string]$NumberFormat="00000: "
    )

    begin{
        [int]$Count=0
    }

    process{
        foreach ($s in $Lines){
            $Count++
            $s = $Count.ToString($NumberFormat) + $s
            Write-Output $s
        }
    }

    end{}
}

パラメーターの文字列の配列を指定した行番号の書式で処理します。
PS > Test-Sample -Lines ("One", "Two", "Three") -NumberFormat "[00] "
[01] One
[02] Two
[03] Three

パイプラインの値をパラメーターとして処理します。
PS > Get-ChildItem $PSHome\*.exe | Test-Sample
00001: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
00002: C:\Windows\System32\WindowsPowerShell\v1.0\powershell_ise.exe

このページの上

パラメーターと同じ名前のパイプラインのプロパティを使う(ValueFromPipelineByPropertyName)

Parameter属性のValueFromPipelineByPropertyName引数を$trueにします。
[Parameter(ValueFromPipelineByPropertyName=$true)]
下記はパラメーターかパイプラインのYearプロパティが、平年か閏年かを表示します。
function Test-Sample
{
    [CmdletBinding()] 
    [OutputType([String])]
    Param ( 
        # 閏年か判定する4桁の年を指定します。
        [Parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
        [ValidateRange(1,9999)]
        [int]$Year = [DateTime]::Now.Year
    )

    if ([DateTime]::IsLeapYear($Year)){
        return("閏年")
    } else {
        return("平年")
    }
}

PS > Get-Date | Test-Sample
平年

PS > Test-Sample 2020
閏年

このページの上

余ったパラメーターを使う(ValueFromRemainingArguments)

Parameter属性のValueFromRemainingArguments引数を$trueにします。
[Parameter(ValueFromRemainingArguments=$true)]
下記のサンプルではCmdletBinding属性のPositionalBindingを$falseにして、位置パラメーターを無効にしてます。($Param1に値を設定するにはパラメーター名が必要です)
function Test-Sample
{
    [CmdletBinding(PositionalBinding=$false)] 
    Param ( 
        [String]$Param1,
        
        [Parameter(ValueFromRemainingArguments=$true)] 
        [String[]]$Param2
    )
    
    Write-Output "`$Param1 = $Param1"
    Write-Output "`$Param2 = $Param2"
}

パラメーター名の-Param1を指定して値を「One」にします。残りの「Tow Three」は$Param2の値になります。
PS > Test-Sample -Param1 One Tow Three
$Param1 = One
$Param2 = Tow Three

パラメーター名を指定しないと位置パラメーターが無効なので、すべて$Param2の値になります。
PS > Test-Sample One Tow Three
$Param1 = 
$Param2 = One Tow Three

パラメーター名の-Param1を指定して値を「One」にします。前の「/a 」と後ろの「/b /c」は$Param2の値になります。
PS > Test-Sample /a -Param1 One /b /c
$Param1 = One
$Param2 = /a /b /c

パラメーター名の-Param1を指定して値を「One」にします。余りがないので$Param2の値は空になります。
PS > Test-Sample -Param1 One
$Param1 = One
$Param2 = 

パラメーターがないと、$Param1と$Param2の値は空になります。
PS > Test-Sample
$Param1 = 
$Param2 =

このページの上

ワイルドカードを使う(SupportsWildcards)

SupportsWildcards属性を使用します。
[SupportsWildcards()]
ワイルドカードは、$PSCmdlet.SessionState.Path.GetResolvedProviderPathFromPSPathメソッドで展開します。
function Test-Sample
{
    [CmdletBinding()]
    Param
    (
        [SupportsWildcards()]
        [String[]]
        $Path="*"
    )
	
    foreach ($File in $Path) {
        $Provider = $null
        Write-Output $PSCmdlet.SessionState.Path.GetResolvedProviderPathFromPSPath($File, [ref]$Provider)
    }
}

表示は環境により異なります。
PS > Test-Sample -Path $PSHome\*.exe, $PSHome\*.ps1
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
C:\Windows\System32\WindowsPowerShell\v1.0\powershell_ise.exe
C:\Windows\System32\WindowsPowerShell\v1.0\Microsoft.PowerShellISE_profile.ps1
C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1

このページの上

関数が返すオブジェクトの型(OutputType)

OutputType属性で関数が返すオブジェクトの型を宣言しますが、型チェックしないので違う場合でもエラーになりません。
[OutputType([String])]の戻り値はString型です。
複数の型がある場合は[OutputType([Boolean], [Int32], [String])]のようにカンマ(,)で区切ります。
パラメーターセット名は[OutputType([String], ParameterSetName="Set1")]のように宣言します。(ParameterSetNameの値は、Parameter属性のParameterSetNameと同じにします)
function Test-Sample
{
    [CmdletBinding()]
    [OutputType([String])]
    Param ( 
        [String]$Name="NoName"
    )
    
    return "Hello $Name !"
}

PS > Test-Sample Suyama
Hello Suyama !

下記はOutputTypeで宣言した型を表示します。
PS > (Get-Command Test-Sample).OutputType
Name          Type          TypeDefinitionAst
----          ----          -----------------
System.String System.String 

Object型以外の配列を返す場合は注意が必要です。
function Test-Sample
{
    [CmdletBinding()]
    [OutputType([Int32[]])]
    Param ()
    [Int32[]]$arr=@(1,2,3)
    return $arr
}

上記の関数の戻り値は、Object[]になります。(Int32型の配列ではありません)
PS > (Test-Sample).GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array

戻り値の変数の前にカンマ(,)を挿入して配列を明示します。
function Test-Sample
{
    [CmdletBinding()]
    [OutputType([Int32[]])]
    Param ()
    [Int32[]]$arr=@(1,2,3)
    return , $arr
}

上記の関数の戻り値は、Int32[]になります。
PS > (Test-Sample).GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Int32[]                                  System.Array

このページの上

動的パラメーター(DynamicParam)

ファイル一覧、レジストリやファイルの値などによって動的にパラメーターを作成します。
DynamicParam{}スクリプトブロック内で、RuntimeDefinedParameterDictionaryクラスの値をreturnします。
パラメーターの属性はParameterAttributeクラスで指定します。 【外部リンク】マイクロソフトのParameterAttributeを参照してください。
ValidateSet検証属性のリストはValidateSetAttributeクラスで指定します。
動的パラメーターの値は$PSBoundParametersで参照します。
下記はパラメーターのGroup1の値によって、動的にパラメーターのstring型のGroup2のリストの値を設定します。
function Test-DynamicParam
{
    [CmdletBinding()]
    [OutputType([Object[]])]
    Param
    (
        [Parameter(Mandatory=$true, Position=0, HelpMessage="Dog, Cat, Hamster のどれかを入力してください。")]
        [ValidateSet("Dog", "Cat", "Hamster")]
        [string]$Group1
    )

    DynamicParam
    {
        $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
        $ParameterAttribute.Mandatory = $true
        $ParameterAttribute.Position = 1

        $AttributeCollection = New-Object 'Collections.ObjectModel.Collection[System.Attribute]'
        $AttributeCollection.Add($ParameterAttribute)

        $list = New-Object 'System.Collections.Generic.List[string]'
        switch ($Group1){ # -CaseSensitiveを指定しないので、英字の大文字と小文字は区別しないで比較します。
            "Dog"   {
                        $list.AddRange( [string[]]@("Chihuahua", "Toy Poodle", "Bulldog"))
                        $ParameterAttribute.HelpMessage = "Chihuahua, Toy Poodle, Bulldog のどれかを入力してください。"
                        break;
                    }
            "Cat"   {
                        $list.AddRange( [string[]]@("Munchkin", "Scottish Fold", "Ragdoll"))
                        $ParameterAttribute.HelpMessage = "Munchkin, Scottish Fold, Ragdoll のどれかを入力してください。"
                        break;
                    }
            "Hamster" {
                        $list.AddRange( [string[]]@("Golden", "Roborovski", "Djungarian"))
                        $ParameterAttribute.HelpMessage = "Golden, Roborovski, Djungarian のどれかを入力してください。"
                        break;
                      }
            default {
                        throw "`$Group1に $Group1 の項目はありません!" # 既にValidateSet属性のエラーになるので、この行は実行されません。
                    }
        }
        
        $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute ($list)
        $AttributeCollection.Add($ValidateSetAttribute)

        $RuntimeDefinedParameter = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameter @("Group2", [string], $AttributeCollection)

        $RuntimeDefinedParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
        $RuntimeDefinedParameterDictionary.Add("Group2", $RuntimeDefinedParameter)

        return $RuntimeDefinedParameterDictionary
    }

    begin{
        $Group2 = $PSBoundParameters["Group2"]
    }

    Process
    {
        Write-Output "パラメーターの`$Group1の値は、「$Group1」です。"
        Write-Output "パラメーターの`$Group2の値は、「$Group2」です。"
    }
}

パラメーターのGroup1の値に「Dog」を選択した場合は、-Group2を入力すると表示されたリストから選択できます。
Test-DynamicParam -Group1 Dog -Group2 
リストの項目はコード順に整列して「Bulldog、Chihuahua、Toy Poodle」が表示されます。(Windows PowerShell ISE)
リスト
コマンドラインでリストの項目にスペースを含む場合は、'Toy Poodle'のように引用符で囲む必要があります。(リストから選択すると自動で引用符で囲まれます)

パラメーターのGroup1の値が「Cat」の場合は、Group2のリストは下記のように表示します。
リスト

パラメーターのGroup1の値が「Hamster」の場合、Group2のリストは下記のように表示します。
リスト


このページの上


次のサンプルは指定したパスのファイルの表示したリストから選択したファイルを、Get-Itemコマンドレットで表示します。
下記はパラメーターのPathの値によって、動的にパラメーターのstring型のFileNameのためのリストを作成します。
function Get-ItemFromList
{
<#
.Synopsis
   指定したパスのファイルの表示したリストから選択したファイルを、Get-Itemコマンドレットで表示します。

.PARAMETER Path
 リストに表示する検索するファイルマスクです。
 省略すると「.\*.*」です。

.PARAMETER FileName
 ファイル名です。
 #>
    [CmdletBinding()]
    [OutputType([System.IO.FileInfo])]
    Param
    (
        [Parameter(Position=0)]
        [string]$Path = ".\*.*"
    )

    DynamicParam
    {
        if ($Path.Length -lt 1){
            $Path = ".\*.*"
        }

        $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
        $ParameterAttribute.Mandatory = $true
        $ParameterAttribute.Position = 1
        $ParameterAttribute.HelpMessage = "ファイル名"

        $AttributeCollection = New-Object 'Collections.ObjectModel.Collection[System.Attribute]'
        $AttributeCollection.Add($ParameterAttribute)
        
        $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute (Get-ChildItem $Path)
        $AttributeCollection.Add($ValidateSetAttribute)

        $RuntimeDefinedParameter = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameter @("FileName", [string], $AttributeCollection)

        $RuntimeDefinedParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
        $RuntimeDefinedParameterDictionary.Add("FileName", $RuntimeDefinedParameter)

        return $RuntimeDefinedParameterDictionary
    }

    begin{
        $FileName = $PSBoundParameters["FileName"]
    }

    Process
    {
        Get-Item -LiteralPath $FileName
    }
}
入力支援の機能によってファイル名の先頭からの文字を入力していくと、一致するファイル名を限定できます。
このサンプルでは下記のようにファイルのリストを拡張子でも抽出できます。
Get-ItemFromList -Path .\a*.ps1 -FileName
「.\a*.ps1」に一致するファイルの一覧のリストを作成し、リストからファイル名を選択できます。(Windows PowerShell ISE)
リスト

このページの上


次のサンプルはインストールしているフォント名のリストから選択したフォント名を表示します。
function Get-FontName
{
<#
.Synopsis
   インストールしているフォント名のリストから選択したフォント名を表示します。

.PARAMETER FontName
 フォント名です。
#>
    [CmdletBinding()]
    [OutputType([string])]
    Param()

    DynamicParam
    {
        $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
        $ParameterAttribute.Mandatory = $true
        $ParameterAttribute.Position = 1
        $ParameterAttribute.HelpMessage = "フォント名"

        $AttributeCollection = New-Object 'Collections.ObjectModel.Collection[System.Attribute]'
        $AttributeCollection.Add($ParameterAttribute)
        
        Add-Type -AssemblyName  System.Drawing
        $FontList = New-Object -Type System.Drawing.Text.InstalledFontCollection
        $List = New-Object 'System.Collections.Generic.List[string]'
        foreach ($FF in $FontList.Families){
            $List.Add($FF.Name);
        }
        $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute ($list)
        $AttributeCollection.Add($ValidateSetAttribute)

        $RuntimeDefinedParameter = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameter @("FontName", [string], $AttributeCollection)

        $RuntimeDefinedParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
        $RuntimeDefinedParameterDictionary.Add("FontName", $RuntimeDefinedParameter)

        return $RuntimeDefinedParameterDictionary
    }

    process{
        $PSBoundParameters["FontName"]
    }
}
インストールしているフォント名のリストから選択できます。
フォントリスト

このページの上