【powershell】Try-Catchのエラーハンドリングについて

⓵はじめに

今回は、Windowsサーバでシェル設計を行う機会があり
その中でTry-Catch構文を使った仕様で少しハマってしまったので
備忘録として失敗した原因と成功した時の結果をアウトプットしたいと思います。

 

⓶Try-Catch構文の基本

Try-Catch構文の仕様は簡単に書くと以下の通りです。

 

try {
例外が発生する可能性のある処理
}
catch {
例外発生時の処理
}

 

⓷検証環境

 

■処理内容

#処理⓵
Get-Content C:\test\test.txt
AAAA
#処理⓶
Get-Content C:\test\test2.txt
BBBB
#処理⓷
Get-Content C:\test\test3.txt
CCCC

 

■出力したいログ

**********************
トランスクリプトが開始されました。出力ファイル: C:\test\log.txt
AAAA
PS>終了エラー(Get-Content): "ユーザー設定変数 "ErrorActionPreference" または共通パラメーターが Stop に設定されているため、実行中のコマンドが停止しました。パス 'C:\test\test2.txt_error' が存在しないため検出できません。"
処理⓶でのエラーです
**********************
Windows PowerShell トランスクリプト終了
終了時刻: XXXXXXXXXXXXXX
**********************

 

■スクリプト要件

・処理について
 ➡処理⓶でエラーが起きた際に処理⓷が実行されていないこと
 ➡出力メッセージでエラー発生箇所が判別可能であること

・ログ
 ➡トランスクリプトを別ファイルに出力

 

⓸検証

 

■Try-Catch処理の確認

 

#######################
#####処理内容(testshell.txt)
#######################

#エラーが起きた際に処理中断されcatchに入るように
$ErrorActionPreference = "Stop"

#トランスクリプトを以下のパスに出力
Start-Transcript -Path "C:\test\log.txt"

try {
    #処理⓵
    Get-Content C:\test\test.txt
    #処理⓶
    Get-Content C:\test\test2.txt_error
    #処理⓷
    Get-Content C:\test\test3.txt
}
catch {
    Write-Host "エラーが発生しました"
}

Stop-Transcript
 

#######################
####出力ログ(log.txt)
#######################

**********************
トランスクリプトが開始されました。出力ファイル: C:\test\log.txt
AAAA
PS>終了エラー(Get-Content): "ユーザー設定変数 "ErrorActionPreference" または共通パラメーターが Stop に設定されているため、実行中のコマンドが停止しました。パス 'C:\test\test2.txt_error' が存在しないため検出できません。"
エラーが発生しました
**********************
Windows PowerShell トランスクリプト終了
**********************

 

基本的な構文だと以下のような結果になりました。

・処理について
 ◎➡処理⓶でエラーが起きた際に処理⓷が実行されていないこと
 ×➡出力メッセージでエラー発生箇所が判別可能であること

・ログ
 ◎➡トランスクリプトを別ファイルに出力

ここから×の箇所を実現したいです。

 

■検証1回目(検証失敗)

 

#######################
#処理確認⓵
#######################

#エラーが起きた際に処理中断されcatchに入るように
$ErrorActionPreference = "Stop"

#トランスクリプトを以下のパスに出力
Start-Transcript -Path "C:\test\log.txt"

try {
    #処理⓵
    Get-Content C:\test\test.txt
    #処理⓶
    Get-Content C:\test\test2.txt_error
    if ($? -ne "True")
    {
        Write-Host "処理⓶でエラーが発生しました"
    }
    #処理⓷
    Get-Content C:\test\test3.txt
}
catch {
    Write-Host "エラーが発生しました"
}

Stop-Transcript

#######################
####出力ログ(log.txt)
#######################

**********************
トランスクリプトが開始されました。出力ファイル: C:\test\log.txt
AAAA
PS>終了エラー(Get-Content): "ユーザー設定変数 "ErrorActionPreference" または共通パラメーターが Stop に設定されているため、実行中のコマンドが停止しました。パス 'C:\test\test2.txt_error' が存在しないため検出できません。"
エラーが発生しました
**********************
Windows PowerShell トランスクリプト終了
**********************

 

「コマンドの戻り値判定でエラーメッセージを出す」仕様にしたところ結果は先ほどと変わらずでした。

・処理について
 ◎➡処理⓶でエラーが起きた際に処理⓷が実行されていないこと
 ×➡出力メッセージでエラー発生箇所が判別可能であること

・ログ
 ◎➡トランスクリプトを別ファイルに出力

 

bashの時に同じ仕様で要件を満たしていましたが、trycatch構文の理解ができていなかったため
実現できていませんでした。

 

原因としては

⓵try文内でエラーが発生した時に処理が中断されcatchに入る
⓶コマンドでエラーが発生➡その次の戻り値判定文の処理も中断される
⓷結果としてcatchのメッセージが表示

のような流れにより処理できませんでした。

 

■検証2回目(検証失敗)

 

#######################
#####処理内容(testshell.txt)
#######################

#エラーが起きた際に処理中断されcatchに入るように
$ErrorActionPreference = "Stop"

#トランスクリプトを以下のパスに出力
Start-Transcript -Path "C:\test\log.txt"

try {
    #処理⓵
    Get-Content C:\test\test.txt
    #処理⓶
    try {
        Get-Content C:\test\test2.txt_error
    }
    catch {
        Write-Host "処理⓶でのエラーです"
        $errflg=1
    }
    #処理⓷
    Get-Content C:\test\test3.txt
}

catch {
    Write-Host "エラーが発生しました"
}

Stop-Transcript

#######################
####出力ログ(log.txt)
#######################

**********************
トランスクリプトが開始されました。出力ファイル: C:\test\log.txt
AAAA
PS>終了エラー(Get-Content): "ユーザー設定変数 "ErrorActionPreference" または共通パラメーターが Stop に設定されているため、実行中のコマンドが停止しました。パス 'C:\test\test2.txt_error' が存在しないため検出できません。"
処理⓶でのエラーです
CCCC
**********************
Windows PowerShell トランスクリプト終了
**********************

 

上記のようにtry文の中にtrycatch文を入れることでメッセージを出す処理を追加しました。
結果は以下のようになりました。

・処理について
 ×➡処理⓶でエラーが起きた際に処理⓷が実行されていないこと
 ◎➡出力メッセージでエラー発生箇所が判別可能であること

・ログ
 ◎➡トランスクリプトを別ファイルに出力

 

出力したいメッセージを出すことはできましたが、
処理⓶でエラーが発生したのに処理が中断されず処理⓷も処理されてしまいました。

 

■検証3回目(検証成功)

 

#######################
#####処理内容(testshell.txt)
#######################

#エラーが起きた際に処理中断されcatchに入るように
$ErrorActionPreference = "Stop"

#トランスクリプトを以下のパスに出力
Start-Transcript -Path "C:\test\log.txt"

try {
    #処理⓵
    Get-Content C:\test\test.txt
    #処理⓶
    try {
        Get-Content C:\test\test2.txt_error
    }
    catch {
        Write-Host "処理⓶でのエラーです"
        $errflg=1
    }
    #処理⓷
    if ($errflg -ne 1)
    {
        try {
            Get-Content C:\test\test3.txt
        }
        catch {
            Write-Host "処理⓷でのエラーです"
        }
    }
}

catch {
    Write-Host "エラーが発生しました"
}

Stop-Transcript

#######################
####出力ログ(log.txt)
#######################

**********************
トランスクリプトが開始されました。出力ファイル: C:\test\log.txt
AAAA
PS>終了エラー(Get-Content): "ユーザー設定変数 "ErrorActionPreference" または共通パラメーターが Stop に設定されているため、実行中のコマンドが停止しました。パス 'C:\test\test2.txt_error' が存在しないため検出できません。"
処理⓶でのエラーです
**********************
Windows PowerShell トランスクリプト終了
**********************

 

処理⓷の実行条件に"処理⓶が正常終了したこと"を追加することで
処理⓶エラー➡処理中断を実現できました。

 

上記のスクリプトでようやく下記要件をクリアできました。

・処理について
 ◎➡処理⓶でエラーが起きた際に処理⓷が実行されていないこと
 ◎➡出力メッセージでエラー発生箇所が判別可能であること

・ログ
 ◎➡トランスクリプトを別ファイルに出力

 

⓹最後に

Bashでも同様の処理を作った後にPowerShellでの構築でしたが、
コマンドレットやエラーハンドリングの仕様が違っており苦戦してしまいました。

ドキュメントを読み込んで仕様を把握することが楽しくなってきたので
また苦戦したことがあればアウトプットしていきたいです!

Last modified: 2023-12-24

Author