エラー処理

try~catch~finally / 自動変数の$?と$ErrorActionPreference / trap / throw

try~catch~finallyステートメント

try~catch~finallyステートメントで、エラー処理ができます。

構文

try {
    <statement list>
}
catch [[<error type>][',' <error type>]*] {
    <statement list>
}
finally {
    <statement list>
}

Sample Script

script.ps1

try {
    Write-Host 'try'
    aaa
}
catch {
    Write-Host 'catch'
    Write-Host $_
}
finally {
    Write-Host 'finally'
}

実行例

PS C:\Users\user\Desktop> .\script.ps1
try
catch
用語 'aaa' は、コマンドレット、関数、スクリプト ファイル、または操作可能なプログラ
ムの名前として認識されません。名前が正しく記述されていることを確認し、パスが含まれ 
ている場合はそのパスが正しいことを確認してから、再試行してください。
finally

エラーの情報のアクセス方法

エラー内容やエラー箇所については、
$_$PSItem$_.ScriptStackTraceなどで確認でます。

scriptと実行例

script

script.ps1

$ErrorActionPreference = "Stop"
try {
    Write-Host "Main:try"
    Get-Content no_exist.txt # 存在しないファイルを開く
}
catch {
    Write-Host "Main:catch"

    Write-Host '$_:' $_
    Write-Host '$PSItem:' $PSItem

    Write-Host '$_.ScriptStackTrace:'
    Write-Host $_.ScriptStackTrace
}

実行例

PS /Users/user/Desktop> ./script.ps1
Main:try
Main:catch
$_: Cannot find path '/Users/user/Desktop/no_exist.txt' because it does not exist.
$PSItem: Cannot find path '/Users/user/Desktop/no_exist.txt' because it does not exist.
$_.ScriptStackTrace:
at <ScriptBlock>, /Users/user/Desktop/script.ps1: line 4
at <ScriptBlock>, <No file>: line 1

コマンドレットのエラー制御を指定する

$ErrorActionPreferenceに値を設定することで、エラー発生時の挙動を指定できます。
PowerShellには、下記の表の通り、終了するエラーと終了しないエラーがあります。

挙動
Continue エラーを表示して、継続する。デフォルト値。
SilentlyContinue エラーを表示しないで、継続する
Ignore SilentlyContinueと同様。しかし、$error配列にエラー内容を追記しない
Inquire ユーザーに対し確認プロンプトを表示する
Stop エラー発生時点で、処理を停止する

終了するエラーはcatchされます。
一方、ContinueSilentlyContinueはcatchされません。

上記のSample Scriptでは、
先頭行で$ErrorActionPreferenceStopにすることで、
エラーをcatchできるようにしています。

対象のコマンドレットのエラー制御を指定する -ErrorAction

全体のエラー制御ではなく、
対象のコマンドレットのエラーを制御したい場合は「-ErrorAction」で指定します。
-ea」という風に省略することもも可能です。

Example

PS C:\Users\user\Desktop> cat no_exist.txt
cat : Cannot find path 'C:\Users\user\Desktop\no_exist.txt' because it does not exist.
At line:1 char:1
+ cat no_exist.txt
+ ~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (C:\Users\user\Desktop\no_exist.txt:String) [Get-Content], ItemNotFoundException
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand
 
PS C:\Users\user\Desktop> cat no_exist.txt -ErrorAction SilentlyContinue
PS C:\Users\user\Desktop>
PS C:\Users\user\Desktop> cat no_exist.txt -ea SilentlyContinue
PS C:\Users\user\Desktop>

trapステートメント

PowerShell V1の頃からあります。

PowerShell V2でtry~catch~finallyステートメントが追加されたため、
trapステートメントを使用する機会は少ないかもしれません。

構文

trap [[<error type>]] {
    <statement list>
}

trapステートメント内にbreakをいれるかcontinueをいれるかで、挙動を指定できます。

  • どちらもなし:エラーを表示して、処理を継続する
  • break:エラーを表示して、処理を終了する
  • continue:エラーを表示しないで、処理を継続する

breakもcontinueもない場合

Script
Write-Host "Pre"
no_exist_command
Write-Host "Post"
 
trap {
    Write-Host "Trap"
}
実行結果

エラーを表示して処理を継続する。

PS C:\Users\user\Desktop> .\script.ps1
Pre
Trap
no_exist_command : The term 'no_exist_command' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the n
ame, or if a path was included, verify that the path is correct and try again.
At C:\Users\user\Desktop\script.ps1:2 char:1
+ no_exist_command
+ ~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (no_exist_command:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException
 
Post

breakの場合

Script
Write-Host "Pre"
no_exist_command
Write-Host "Post"
 
trap {
    Write-Host "Trap"
    break # 変更点
}
実行結果

エラーを表示して、処理を終了する。

PS C:\Users\user\Desktop> .\script.ps1
Pre
Trap
The term 'no_exist_command' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At C:\Users\user\Desktop\script.ps1:2 char:1
+ no_exist_command
+ ~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (no_exist_command:String) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : CommandNotFoundException

continueの場合

Script
Write-Host "Pre"
no_exist_command
Write-Host "Post"
 
trap {
    Write-Host "Trap"
    continue # 変更点
}
実行結果

エラーを表示しないで、処理を継続する。

PS C:\Users\user\Desktop> .\script.ps1
Pre
Trap
Post

エラーを発生させるthrow

エラーのthrow

PS> throw "Error!" # エラーメッセージを表示させる
Error!
At line:1 char:1
PS> throw New-Object System.NullReferenceException # 指定のエラーを発生させる
オブジェクト参照がオブジェクト インスタンスに設定されていません。
At line:1 char:1

エラーの再throw

下位関数内のcatchの中でthrowすると、上位の関数のcatchにエラーを渡すことができます。

function Sub {
    try {
        Write-Host "Sub :try"
        throw (Get-Date).ToString("yyyy/MM")    # 最初のthrow
    }
    catch {
        Write-Host "Sub :catch"
        throw    # 再throw
    }
}
 
function Main {
    try {
        Write-Host "Main:try"
        Sub
    }
    catch {
        Write-Host "Main:catch"
        Write-Host $_    # 受け取ったエラーを出力する
    }
    finally {
        Write-Host "Main:finally"
    }
}
 
Main
PS C:\Users\user\Desktop> .\script.ps1
Main:try
Sub :try  
Sub :catch
Main:catch
2020/11   
Main:finally
  • 再throwには、throwがオススメ。
  • 再throw時にthrow "Error Message"などとすると、エラーの個数($error.Count)が余分に1つ増えてしまうのでやめたほうが良いかもしれません。

stackoverflow How can I Rethrow an exception from catch block in Powershell?

エラー処理に関する自動変数

ここでは以下の3つの自動変数について見ていきます。

  • $?
  • $LastExitCode
  • $Error

$?と$LastExitCode

エラーに関する自動変数として、
自動変数の$?$LastExitCodeがあります。

2つの自動変数はそれぞれ異なる情報を格納します。
$?は、最後の処理の結果が成功ならTrueを、失敗ならFalseを格納します。
$LastExitCodeは、Windowsの実行ファイルの終了コードを格納します。

about_Automatic_Variables | Microsoft TechNet

$?
   Contains the execution status of the last operation.
   It contains TRUE if the last operation succeeded and FALSE if it failed.
$LastExitCode
   Contains the exit code of the last Windows-based program that was run.

Windowsの実行ファイルでない場合

# エラーになる例
PS> $?
True    # 初期状態
PS> $LASTEXITCODE
PS>     # 初期状態はNull
PS> $date = [datetime]"a"
~エラーになる~
PS> $?
False   # 失敗を格納している。変化した
PS> $LASTEXITCODE
PS>     # 失敗を格納していない。変化していない、Nullのまま

Windows実行ファイルの場合 ping.exeの例

# 成功する例
PS> $?
False   # 前回失敗している
PS> $LASTEXITCODE
1       # 前回失敗の終了コード
PS> ping EXIST_PC

-中略(成功)- 

PS> $?
True    # 成功を格納している。変化した
PS> $LASTEXITCODE
0       # 成功を格納している。変化した

# エラーになる例
PS> ping NO_EXIST_PC
ping 要求ではホスト NO_EXIST_PC が見つかりませんでした。ホスト名を確認してもう一度実行してください。
PS> $?
False   # 失敗を格納している。変化した
PS> $LASTEXITCODE
1       # 失敗を格納している。変化した

まとめ

  • 直前のエラーを拾いたい場合は$?を使用する
  • Windowsのexeの終了コードを拾いたい場合は$LastExitCodeを使用する

$Error

$Errorは、発生したエラーの情報を格納する自動変数です。
$Error[0]で、最新のエラーを出力できます。
$Error[-1]で、最古のエラーを出力できます。

PS> $Error      # エラーが発生していない状態
PS> throw 'Error!'
Exception: Error!
PS> throw 'Error!!'
Exception: Error!!
PS> throw 'Error!!!'
Exception: Error!!!
PS> $Error      # 発生したエラーの情報を出力する

Exception: Error!!!

Exception: Error!!

Exception: Error!
PS> $Error[0]   # 最新のエラーを出力する

Exception: Error!!!
PS> $Error[-1]  # 最古のエラーを出力する

Exception: Error!

エラーの型を調べる

以下のようにエラーの型のfullnameを確認すると、エラーの型を調べられます。

  • $_.exception.gettype().fullname
  • $error[0].exception.gettype().fullname

Step1 実際にエラーの型を調べる

エラーの型を調べるために、
catchブロックの中で$_.exception.gettype().fullnameを出力します。

function Main {
    try {
        throw New-Object System.NullReferenceException
    }
    catch {
        $_.exception.gettype().fullname
    }
}
 
Main
PS> .\script.ps1
System.NullReferenceException

エラーの型が、System.NullReferenceExceptionであることがわかりました。

Step2 調べたエラーの型でエラーをcatchする

実際に上記のエラーの型をcatchの条件に組み込んで、catchできるか確かめます。

function Main {
    try {
        throw New-Object System.NullReferenceException
    }
    catch [System.NullReferenceException] {
        Write-Host "Catch"
    }
    catch {
        Write-Host "Catch other"
    }
}

Main

Step3 結果

狙ったエラーをcatchできました。

PS> .\script.ps1
Catch   # 狙ったエラーをcatchできた

YouTube

動画による説明はこちら。

参考資料

About Try Catch Finally | Microsoft Docs

About Trap | Microsoft Docs

about_Automatic_Variables | Microsoft TechNet

How can I rethrow an exception from catch block in PowerShell? | stackoverflow


スポンサーリンク

最終更新 2022/10/07: update (71568f4)