引数の受け取りにparamを使おう

PowerShellはスクリプト(ps1ファイル)や関数の引数をargsで受け取ることができますが、paramを使えばすっきりします。

PowerShellらしさをもとめるならばparamは必須です。引数を受け取るのと同時に引数に名前をつけることができます。

スクリプト(ps1)の引数をparamで受け取る

argsで受け取る場合(芋っぽい)

#C:\aaa\test.ps1

$ErrorActionPreference = "Stop"

$nenrei = $args[0]
$name = $args[1]
$seibetsu = $args[2]

Write-Output "年齢:$($nenrei) 名前:$($name) 性別:$($seibetsu)"
実行形式
C:\aaa\test.ps1 10 TSUBASA Male

結果
年齢:10 名前:TSUBASA 性別:Male

paramで受け取る場合

#C:\aaa\test.ps1

param($nenrei, $name, $seibetsu)

$ErrorActionPreference = "Stop"
Write-Output "年齢:$($nenrei) 名前:$($name) 性別:$($seibetsu)"
実行形式 その1
C:\aaa\test.ps1 10 TSUBASA Male

結果
年齢:10 名前:TSUBASA 性別:Male

実行形式 その2 (引数の名前付きで呼び出せる)
C:\aaa\test.ps1 -name TSUBASA -seibetsu Male -nenrei 10 

結果
年齢:10 名前:TSUBASA 性別:Male

paramの特徴

paramは関数ではありません。コメントを除きコマンドの先頭に記述しないとエラーになります。

例えば、$ErrorActionPreference = “Stop”の後にparamを書くとエラーになります。

paramと引数の数が異なる場合

実際の引数が3つで、param($opt1, $opt2) とすると、引数が1つ余ります。

余った引数はargsに格納されます。paramを行わないとargsは、args[0]~args[2]ですが、paramで2つ引数を受け取ると、argsは、args[0]のみになり、引数の3番目が格納されます。

実際の引数が2つで、param($opt1, $opt2, $opt3)とすると、$opt3には$nullが設定されます。

可変引数をparamで受け取る場合

スクリプト(ps1)によっては引数の数が決まっていない場合があり、例えば引数でファイルを1つ渡されたらそのファイルだけ処理し、引数でファイルを2つ渡されたらファイル2つ分を処理するような場合、引数は可変になります。

結論として、可変引数はargsで受けるしかなさそうです。

第1引数と第2引数は固定で、第3引数以降が可変の場合、

param($opt1, $opt2) で固定部分を受けて、残りの引数がargsに格納されるので、[array]$opt3 = args などとして、可変部分を取得します。

#C:\aaa\test.ps1

param($nenrei, $name, $seibetsu)

$ErrorActionPreference = "Stop"
[array]$array = $args
Write-Output "年齢:$($nenrei) 名前:$($name) 性別:$($seibetsu)"

foreach ($data in $array) {
    $data
}
実行
C:\aaa\test.ps1 10 TSUBASA Male tel1 tel2

結果
年齢:10 名前:TSUBASA 性別:Male
tel1
tel2

関数の引数をparamで受け取る

スクリプトと同じです。

#C:\aaa\test.ps1

param($nenrei, $name, $seibetsu)

function Test2 {
    param($nenrei, $name)
    Write-Output "年齢:$($nenrei) 名前:$($name)"
}

$ErrorActionPreference = "Stop"
Test2 $nenrei $name
実行
C:\aaa\test.ps1 10 TSUBASA Male

結果
年齢:10 名前:TSUBASA

スクリプト(ps1)で受けた可変引数を関数に可変引数として渡す

分配演算子(@)を使います。

#C:\aaa\test.ps1

param($nenrei, $name, $seibetsu)

function Test2 {
    param($nenrei, $name)
    [array]$tels = $args
    Write-Output "年齢:$($nenrei) 名前:$($name)"
    foreach ($tel in $tels) {
        Write-Output "TEL:$($tel)"
    }
}

$ErrorActionPreference = "Stop"
[array]$array = $args
Test2 $nenrei $name @array
実行
実行
C:\aaa\test.ps1 10 TSUBASA Male Tel1 Tel2

結果
年齢:10 名前:TSUBASA
TEL:Tel1
TEL:Tel2

分配演算子は、配列の中身を列挙した形で関数の引数に渡します。分配演算子は引数に受け渡すときしか使用できません。

なお、@は配列の定義や連想配列の定義にも使われます。

$hairetsu = @("001","002")
$hairetsu[0]
$renso = @{001="AAA";002="BBB}
$renso.001

結果
001
AAA

paramの入力チェック

paramは必須チェックや値の範囲チェックやカスタムチェックを行うことができ、チェックの結果エラーだと例外が発生します(必須チェックの[Parameter(Mandatory=$true)]は値の入力を促される)。

# C:\aaa\test.ps1

param($nenrei, 
    $name, 
    [ValidateSet("otoko", "onna")]
    [string]
    $seibetsu)

$ErrorActionPreference = "Stop"
Write-Output "年齢:$($nenrei) 名前:$($name) 性別:$($seibetsu)"
実行
c:\aaa\test.ps1 10 TSUBASA hatena

結果
c:\aaa\test.ps1 : パラメーター 'seibetsu の引数を確認できません。引数 "hatena" は、ValidateSet 属性で指定されたセット "otoko,onna" に属していません。このセットの引数を指定して
、コマンドを再度実行してください。
発生場所 行:1 文字:23
・・・

たいていのバッチ処理は例外をそのままメッセージに出力することはなく、なにかしら加工して出力することが多いです。

関数の例外はメインのスクリプトでcatchできるので問題ありませんが、メインのスクリプトのparamで発生した例外はcatchできないため、メインのスクリプトではparamの入力チェック機能を使用しないほうが無難でしょう。

また、必須チェック([Parameter(Mandatory=$true)])は必須項目に値が設定されていないと、値の入力を促されるのでバッチ処理を作成する際は使用しないほうがよいでしょう。代わりに[ValidateScript({})]を使用して$nullや空の場合に例外を発生させるチェックを記述したほうがよいでしょう。

入力チェックに使える属性(詳細は割愛)

属性説明
[Parameter(Mandatory=$true)]必須チェック。
エラー時は再入力
[ValidateNotNullOrEmpty()]$nullまたは””の場合例外
[ValidateNotNull()]$nullの場合例外
[ValidateCount(1, 2)]配列の数が範囲外の場合例外
[ValidateLength(1, 4)]stringの桁数が範囲外の場合例外
[ValidateRange(50, 100)]数値が範囲外の場合例外
[ValidateSet(“otoko”, “onna”)]値がリスト外の場合例外
[ValidatePattern(“”^aa$”)]値が正規表現と不一致の場合例外
[ValidateScript({if ($_ -ne “”) {$true} else {$false}})チェック処理を自作する。最後成功時は$trueを返却する