エラー処理

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


スポンサーリンク