PowerShellで関数を定義する方法 – 通常の関数と名前付きブロック

こんにちは。
けいぞうです。

PowerShellで関数を定義する方法についての記事です。

[目次]

関数を定義する方法

PowerShellで関数を定義するには以下のように記述します。

function 関数名([型]$引数1, [型]$引数2 ,[型]$引数3)
{
    #関数の内容
}

また、定義した関数を呼び出すには以下のように記述します。

関数名 $引数1 $引数2 $引数3

例えば、渡した3つの引数の足し算の結果を表示する関数は、以下のように記述します。

function CalSum([int]$a,[int]$b,[int]$c)
{
    $sum = $a + $b + $c
    Write-Output $sum
}

CalSum 1 2 3

PowerShellはスクリプト言語なので、基本的には上記のように本文の頭に関数定義をします。

戻り値について

先ほどの足し算結果を表示してくれる関数ですが、結果を戻り値としてリターンするには以下のように記述します。

function CalSum([int]$a,[int]$b,[int]$c)
{
    $sum = $a + $b + $c
    return $sum
}

$result = CalSum 1 2 3

この場合、$resultに戻り値の「6」が格納されます。

ただし、このPowerShellの戻り値には多くの落とし穴があります。例えば、以下のように記述した場合。

function CalSum([int]$a,[int]$b,[int]$c)
{
    $sum = $a + $b + $c
    Write-Output "足し算の結果を返します。"
    return $sum
}

$result = CalSum 1 2 3

直感的には、関数が呼ばれたときに標準出力に「足し算の結果を返します」が表示されて、戻り値に「6」が返ってきそうですが、実際にはこうなります。

> $result
足し算の結果を返します。
6
> $result.GetType()

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

このように、標準出力の結果が戻り値に含まれてしまっています。

ここについてはPowerShellの関数に関する問題についてまとめた記事にて詳しく解説しています。

名前付きブロック

PowerShellには名前付きブロックというものが存在しています。

名前付きブロックとは簡単に説明するとこんな感じです

  • 関数の中で定義するブロックのこと
  • 名前付きブロックの種類は4種類
  • それぞれの種類によって使われ方・処理のされ方が異なる
  • 名前付きブロックを使う関数にはパイプラインでパラメータを渡す

4種類の名前付きブロックとは⇩です。

種類説明
begin最初に1回だけ実行される
process渡されるオブジェクト毎に1回ずつ実行される
end最後に1回だけ実行される
dynamicparam引数を動的に構築する際に使用する

よく分からないと思うので、簡単な例文を示します。

function test()
{
    begin { Write-Output "開始" }
    process { Write-Output "hoge" }
    end { Write-Output "終了" }
}

1,2,3 | test
開始
hoge
hoge
hoge
終了

このように、beginで書かれた部分はパイプラインで渡されたパラメータの処理をする前に1度だけ、endで書かれた部分は処理が終わって最後に1度だけ、processはパイプラインで渡されたパラメータ毎に1回だけ処理されます。

(※dynamicparamについてはかなり特殊なので、別の記事にて解説予定)

for文などの繰り返しに似ていますが、パラメータに色々なものを渡すことができるので、使い方によって可能性無限大です。


例えば下記のように書くと、関数に渡したパラメータがint型とそれ以外の配列に振り分けられて返ってきます。

function test()
{
    begin
    {
        $int_list = New-Object System.Collections.ArrayList
        $str_list = New-Object System.Collections.ArrayList 
    }

    process
    {
        if($_.GetType().Name -eq "Int32")
        {
            $int_list.Add($_) | Out-Null
        }
        else
        {
            $str_list.Add($_) | Out-Null
        }
    }

    end
    {
        $ht = @{ IntList=$int_list; StrList=$str_list}
        return $ht
    }
}

$result = 1,2,3,"hoge","fuga" | test
$result

Name                           Value
----                           -----
StrList                        {hoge, fuga}
IntList                        {1, 2, 3}


> $result["StrList"]
hoge
fuga
> $result["IntList"]
1
2
3