PowerShell で try – catch – finally

# この記事は以前 C#と諸々 に書いた記事 (を一部修正したもの) です。

 

PowerShell V1.0 では、C# の try – catch – finally のような構造化例外処理がサポートされていません。しかし、trap ブロック、throw ステートメント、break ステートメント、continue ステートメント、そしてスコープについて理解すれば、try – catch – finally を擬似的に再現することができます。

というわけで、次のコードのように C# の try – catch – finally に近い例外処理を実現する関数を書きました。

try {
    # 処理
} catch ([例外の型]) {
    param($ex)
    # 例外処理
} finally {
    # 後処理
}

使用する際は次の点に気を付けてください。

  • 各ブロックの開始の "{" の前と各ブロックの終了の "}" の後ろは、上記のように、改行せずに記述する必要があります。
  • 例外の型は、上記のように、必ず "()" で囲む必要があります。
  • catch または finally は、省略可能です。例外の型も省略可能です。
  • catch ブロック内では、break ステートメントを使用して例外を再スローすることができます。当然、任意の例外を throw ステートメントでスローすることもできます。

で、これらを実現するための関数がこちらです。

function global:try
{
    $currentArgIndex = 0;
    $tryBlock = $args[$currentArgIndex];
    $currentArgIndex++;
    if ($tryBlock -isnot [System.Management.Automation.ScriptBlock])
    {
        throw New-Object "ArgumentException" @("try ブロックの指定が不正です。");
    }
    if ("catch" -eq $args[$currentArgIndex])
    {
        $currentArgIndex++;
        if ($args[$currentArgIndex] -is [Type])
        {
            $targetExceptionType = $args[$currentArgIndex];
            $currentArgIndex++;
        }
        $catchBlock = $args[$currentArgIndex];
        $currentArgIndex++;
        if ($catchBlock -isnot [System.Management.Automation.ScriptBlock])
        {
            throw New-Object "ArgumentException" @("catch ブロックの指定が不正です。");
        }
    }
    if ("finally" -eq $args[$currentArgIndex])
    {
        $currentArgIndex++;
        $finallyBlock = $args[$currentArgIndex];
        $currentArgIndex++;
        if ($finallyBlock -isnot [System.Management.Automation.ScriptBlock])
        {
            throw New-Object "ArgumentException" @("finally ブロックの指定が不正です。");;
        }
    }
    if (($() -eq $catchBlock) -and ($() -eq $finallyBlock))
    {
        throw New-Object "ArgumentException" @("catch ブロックまたは finally ブロックを指定してください。");
    }
    &{
        $requireFinally = ($() -ne $finallyBlock);
        &{
            &$tryBlock;
            trap
            {
                if ($() -eq $catchBlock)
                {
                    break;
                }
                $ex = $_.Exception;
                if (($() -ne $targetExceptionType) -and (!$targetExceptionType.IsAssignableFrom($ex.GetType())))
                {
                    break;
                }
                &$catchBlock $ex;
                continue;
            }
        };
        if ($requireFinally)
        {
            $requireFinally = $False;
            &$finallyBlock;
        }
        trap
        {
            if ($requireFinally)
            {
                $requireFinally = $False;
                &$finallyBlock;
            }
            break;
        }
    };
}

以下のスクリプトを実行すると、動作が確認できます。

try {
    "try ブロック実行";
    throw New-Object "ArgumentException";
    "この文は出力されない";
} catch ([ArgumentException]) {
    param ($ex)
    "{0} がスローされたから catch ブロック実行" -f $ex.GetType().Name;
} finally {
    "finally ブロック実行";
}

出力は以下のようになります。

try ブロック実行
ArgumentException がスローされたから catch ブロック実行
finally ブロック実行

なお、PowerShell V2.0 では、try – catch – finally が標準でサポートされるようです。なので、V2.0 ではこの関数は不要になるのですが、もし V1.0 で try – catch – finally を使用したいという方はぜひこの関数を試してみてください。

コメント

  1. HIRO より:

    よこけんさんの、このテクニックいいですよね。

    PowerShellだと通常、trapを使用しますが、VB.NETやC#を使っている方にとっては、try~catchの方が非常になじみやすいですよね。

    非常に勉強になります

タイトルとURLをコピーしました