- 05/23/2020
- 14分読む
-
- j
- f
- s
エラー処理は、コードを書くことになると人生の一部に過ぎません。 私たちはしばしば、期待される動作の条件を確認して検証することができます。 予期しないことが起こったら、例外処理に目を向けます。, 他の人のコードによって生成された例外を簡単に処理することも、他の人が処理するために独自の例外を生成することもできます。
Note
この記事のオリジナルバージョンは、@KevinMarquetteによって書かれたブログに掲載されました。 ThePowerShellのチームは私達とこの内容を共有するためのケビンに感謝します。 彼のブログをご覧くださいatPowerShellExplained.com.
基本的な用語
これにジャンプする前に、いくつかの基本的な用語をカバーする必要があります。
Exception
例外は、通常のエラー処理が問題を処理できない場合に作成されるイベントのようなものです。,数値をゼロで除算しようとしたり、メモリが不足したりすることは、anexceptionを作成するものの例です。 使用しているコードの作成者が、特定の問題が発生したときに例外を作成することがあります。
Throw and Catch
例外が発生すると、例外がスローされると言います。 スローされた例外を処理するには、キャッチする必要があります。 例外がスローされ、それが何かによってキャッチされない場合、スクリプトはstopsexecutingを停止します。
コールスタック
コールスタックは、互いに呼び出された関数のリストです。, 関数が呼び出されると、それはスタックまたはリストの先頭に追加されます。 関数が終了したり戻ったりすると、スタックから削除されます。
例外がスローされると、例外ハンドラがキャッチするためにその呼び出しスタックがチェックされます。
終了エラーと非終了エラー
例外は通常、終了エラーです。 スローされた例外がキャッチされるか、現在の実行を終了します。 デフォルトでは、Write-Error
によって非終端エラーが生成され、例外をスローせずに出力ストリームにエラーが追加されます。,Write-Error
およびその他の終端エラーはcatch
をトリガーしないため、これを指摘します。
例外を飲み込む
これは、エラーをキャッチしてそれを抑制するときです。 それは非常に困難な問題を撮影することができますので、注意してこれを行います。
基本的なコマンド構文
PowerShellで使用される基本的な例外処理構文の概要を次に示します。
Throw
独自の例外イベントを作成するには、throw
キーワードで例外をスローします。,
function Start-Something{ throw "Bad thing happened"}
これにより、終了エラーである実行時例外が作成されます。 それはによって処理されますcatch
acalling関数内またはこのようなメッセージでスクリプトを終了します。
Write-Error-ErrorAction Stop
私はWrite-Error
デフォルトで終了エラーをスローしないことを述べました。 -ErrorAction Stop
を指定すると、Write-Error
は終了エラーを生成し、catch
で処理できます。,
Write-Error -Message "Houston, we have a problem." -ErrorAction Stop
-ErrorAction Stop
このように使用することについて思い出させてくれたLee Daileyに感謝します。
Cmdlet-ErrorAction Stop
任意の高度な関数またはコマンドレットで-ErrorAction Stop
を指定すると、すべてのWrite-Error
ステートメントは、実行を停止するか、またはcatch
で処理できるエラーを終了するようになります。,
Start-Something -ErrorAction Stop
Try/Catch
PowerShell(および他の多くの言語)で例外処理が機能する方法は、まずtry
コードのセクションとして、エラーがスローされた場合はcatch
ここに簡単なサンプルがあります。
catch
スクリプトは、終了エラーがある場合にのみ実行されます。 もしtry
が正しく実行されると、それはcatch
をスキップします。,
Try/Finally
エラーを処理する必要はありませんが、exceptionhappensかどうかにかかわらず、実行するためのコードが必要な場合があります。 Afinally
スクリプトはまさにそれを行います。
この例を見てください。
リソースを開いたり、リソースに接続したりするときはいつでも閉じる必要があります。 ExecuteNonQuery()
が例外をスローした場合、接続は閉じられません。 これはtry/finally
ブロック内の同じコードです。
この例では、エラーが発生すると接続が閉じられます。 また、エラーがない場合は閉じられます。, finally
スクリプトは毎回実行されます。
あなたは例外をキャッチしていないので、それはまだ呼び出しスタックに伝播されます。
Try/Catch/Finally
使用することは完全に有効ですcatch
そしてfinally
一緒に。 ほとんどの場合、どちらか一方を使用しますが、両方を使用するシナリオが見つかる場合があります。
$PSItem
これで基本がわかったので、もう少し深く掘り下げることができます。,
catch
ブロック内には、例外の詳細を含むタイプの自動変数($PSItem
または$_
)があります。ErrorRecord
。 ここにkeypropertiesのいくつかの速い概観はある。これらの例では、無効なパスを使用しましたReadAllText
この例外を生成します。
::ReadAllText( '\\test\no\filefound.log')
PSItem.ToString()
これにより、ロギングと一般的な出力で使用する最もクリーンなメッセージが得られます。, ToString()
は、$PSItem
が文字列内に配置されている場合に自動的に呼び出されます。
catch{ Write-Output "Ran into an issue: $($PSItem.ToString())"}catch{ Write-Output "Ran into an issue: $PSItem"}
$PSItem.InvocationInfo
このプロパティには、例外がスローされた関数またはスクリプトに関するPowerShellによって収集された追加情報が含まれます。 以下は、Icreatedサンプル例外のInvocationInfo
です。
ここで重要な詳細は、コードのScriptName
、Line
、およびScriptLineNumber
呼び出しが開始された場所を示しています。
$PSItem.,ScriptStackTrace
このプロパティは、例外が生成されたコードに到達した関数呼び出しの順序を示します。
私は同じスクリプトで関数を呼び出すだけですが、multiplescriptsが含まれている場合、これは呼び出しを追跡します。
$PSItem.Exception
これはスローされた実際の例外です。
$PSItem.例外だMessage
これは例外を説明する一般的なメッセージであり、whentroubleshootingの良い出発点です。 ほとんどの例外はしてデフォルトのメッセージが設定することができるものカスタムwhenthe例外がスローされます。,
PS> $PSItem.Exception.MessageException calling "ReadAllText" with "1" argument(s): "The network path was not found."
これは、$PSItem.ToString()
ErrorRecord
に設定されていない場合に返されるメッセージでもあります。
$PSItem.例外だInnerException
例外には内部例外を含めることができます。 これは、呼び出しているコードが例外をキャッチし、別の例外をスローする場合がよくあります。 元の例外は新しい例外の内側に配置されます。
PS> $PSItem.Exception.InnerExceptionMessageThe network path was not found.
私は例外を再スローすることについて話すとき、私は後でこれを再訪します。
$PSItem.例外だ,StackTrace
これは例外のStackTrace
です。 上記のScriptStackTrace
を示しましたが、これはマネージコードへの呼び出し用です。
at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)at System.IO.StreamReader..ctor(String path, Encoding encoding, Boolean detectEncodingFromByteOrderMarks, Int32 bufferSize, Boolean checkHost)at System.IO.File.InternalReadAllText(String path, Encoding encoding, Boolean checkHost)at CallSite.Target(Closure , CallSite , Type , String )
このスタックトレースは、マネージコードからイベントがスローされたときにのみ取得されます。 Aに電話するNETframeworkは直接機能するので、この例で見ることができるのはそれだけです。 一般的に、スタックトレースを探しているときは、コードが停止し、システムコールが開始される場所を探しています。
例外を扱う
基本的な構文と例外プロパティよりも例外に多くのものがあります。,
型付き例外をキャッチする
キャッチした例外を選択することができます。 例外にはタイプがあり、キャッチする例外のタイプを指定することができます。
例外タイプは、各catch
ブロックに一致するものが見つかるまでチェックされますexception.It例外は他の例外から継承できることを認識することが重要です。 上記の例では、FileNotFoundException
はIOException
を継承しています。 したがって、IOException
が最初であれば、代わりに呼び出されます。, 複数の一致があっても、一つのcatchブロックのみが呼び出されます。
System.IO.PathTooLongException
がある場合、IOException
一致しますが、InsufficientMemoryException
がある場合、何もそれをキャッチせず、スタックを伝播します。
一度に複数のタイプをキャッチ
同じcatch
ステートメントで複数の例外タイプをキャッチすることができます。
ありがとう/u/Sheppard_Ra
この追加を提案してくれました。
型付き例外のスロー
PowerShellで型付き例外をスローできます。, 代わりに呼び出すのthrow
文字列で:
throw "Could not find: $path"
このような例外アクセラレータを使用します:
throw "Could not find: $path"
しかし、あなたはそのようにそれを行うときにメッセージを指定する必要があります。
スローされる例外の新しいインスタンスを作成することもできます。 システムにはすべての組み込み例外のデフォルトメッセージがあるため、このメッセージは省略可能です。
throw ::new()throw ::new("Could not find path: $path")
PowerShell5.0以降を使用していない場合は、古いNew-Object
アプローチを使用する必要があります。,
型付き例外を使用すると、前のセクションで説明したように、あなた(または他の人)は型によって例外をキャッチできます。
Write-Error-Exception
これらの型指定された例外をWrite-Error
に追加することができますが、catch
exceptiontypeによるエラー。 使用Write-Error
これらの例のように:
その後、我々はこのようにそれをキャッチすることができます:
catch { Write-Log $PSItem.ToString()}
.NET例外の大きなリスト
私は、Reddit/r/PowerShellコミュニティの助けを借りて、マスターリストをコンパイルしました。,この投稿を補完するネット例外。
- .net例外の大きなリスト
まず、mysituationに適していると思われる例外をそのリストで検索します。 ベースSystem
名前空間で例外を使用しようとする必要があります。
例外はオブジェクトです
多くの型付き例外を使用し始める場合は、それらがオブジェクトであることを忘れないでください。 異なる例外異なるコンストラクタとプロパティを持つ。, System.IO.FileNotFoundException
のFileNotFoundExceptiondocumentationを見ると、メッセージとfilepathを渡すことができることがわかります。
::new("Could not find file", $path)
そして、そのファイルパスを公開するFileName
プロパティを持っています。
catch { Write-Output $PSItem.Exception.FileName}
他のコンストラクタとオブジェクトプロパティについては、.NETのドキュメントを参照してください。
例外を再スローする
catch
ブロックがthrow
同じ例外であれば、catch
それをしないでください。, あなたはcatch
ithappens時に何らかのアクションを処理または実行する予定の例外のみを使用する必要があります。
例外に対してアクションを実行したいが、下流のsosomethingがそれを処理できる例外を再スローする場合があります。 このメッセージを書き込むにはログの問題でありwediscoverでも取扱いの一層のスタックです。
catch{ Write-Log $PSItem.ToString() throw $PSItem}
興味深いことに、throw
catch
内からcatch
を呼び出すことができ、currentexceptionを再スローします。,
catch{ Write-Log $PSItem.ToString() throw}
ソーススクリプトや行番号などの元の実行情報を保持するために、例外を再スローします。 この時点で新しい例外をスローすると、例外が開始された場所が非表示になります。
新しい例外を再スローする
例外をキャッチしたが、別の例外をスローしたい場合は、originalexceptionを新しい例外の中にネストする必要があります。 これにより、スタックの下の誰かが$PSItem.Exception.InnerException
としてアクセスできるようになります。
catch{ throw ::new('Could not access field',$PSItem.Exception)}
$PSCmdlet.,ThrowTerminatingError()
生の例外に対してthrow
を使用することが好きではないことの一つは、throw
ステートメントでエラー messagepointsがあり、その行が問題のある場所であることを示していることです。
エラーメッセージが表示されると、throw
31行目にthrow
Dexter Dhamiは、ThrowTerminatingError()
を使用して修正できることを指摘しました。,
ThrowTerminatingError()
がGet-Resource
という関数内で呼び出されたと仮定すると、これは表示されるエラーです。
問題の原因としてGet-Resource
関数をどのように指しているかわかりますか? えtheuser何かと便利です。
$PSItem
はErrorRecord
であるため、ThrowTerminatingError
この方法で再スローすることもできます。,
catch{ $PSCmdlet.ThrowTerminatingError($PSItem)}
これにより、エラーの原因がコマンドレットに変更され、コマンドレットのユーザーから関数の内部が非表示になります。
Tryは終了エラーを作成できます
Kirk Munroは、一部の例外はtry/catch
ブロック内で実行された場合にのみ終了エラー ここでは、ゼロによる除算の実行時例外を生成する彼が私に与えた例があります。
function Start-Something { 1/(1-1) }
次に、このように呼び出して、エラーが生成され、メッセージが出力されることを確認します。,
&{ Start-Something; Write-Output "We did it. Send Email" }
しかし、同じコードをtry/catch
内に配置することによって、何か他のことが起こることがわかります。
try{ &{ Start-Something; Write-Output "We did it. Send Email" }}catch{ Write-Output "Notify Admin to fix error and send email"}
エラーは終了エラーになり、最初のメッセージは出力されません。 私がこれについて好きではないのは、このコードを関数内に持つことができ、誰かがtry/catch
を使用している場合、それは異なる動作をするということです。
私はこれで問題に遭遇したことはありませんが、それは注意すべきコーナーケースです。
$PSCmdlet.,Throwterminatingerror()inside try/catch
$PSCmdlet.ThrowTerminatingError()
のニュアンスは、yourCmdlet内で終了エラーを作成しますが、コマンドレットを離れた後に非終了エラーに変わります。 これにより、エラーの処理方法を決定するために関数の呼び出し元に負担がかかります。 彼らは、-ErrorAction Stop
を使用するか、try{...}catch{...}
内から呼び出すことで、終了エラーに戻すことができます。,
パブリック関数テンプレート
私がKirk Munroとの会話で持っていた最後の方法は、彼がtry{...}catch{...}
すべてのbegin
、process
およびend
彼のすべてのadvancedfunctionsにブロックを配置することでした。 これらの一般的なcatchブロックでは、$PSCmdlet.ThrowTerminatingError($PSItem)
を使用して、関数を残すすべての例外を処理する単一の行があります。
function Start-Something{ param() process { try { ... } catch { $PSCmdlet.ThrowTerminatingError($PSItem) } }}
すべてがtry
関数内のステートメントにあるため、すべてが一貫して機能します。, Thisalsoをクリーンミラーをエンドユーザーにと皮内部コードから発生するエラーになります。
トラップ
私はtry/catch
例外の側面に焦点を当てました。 しかし、これをまとめる前に、私が言及する必要があるレガシー機能があります。
trap
は、そのスコープで発生するすべての例外をキャッチするためのスクリプトまたは関数に配置されます。 例外が発生すると、trap
のコードが実行され、通常のコードが続行されます。 複数の例外が発生した場合、トラップは何度も呼び出されます。,私は個人的にこのアプローチを採用したことはありませんが、すべての例外をログに記録するadminまたはcontrollerスクリプトの値を見ることができます。
閉会の挨拶
スクリプトに適切な例外処理を追加すると、スクリプトがより安定しているだけでなく、これらの例外をトラブルシューティングするのが容易になります。
私は多くの時間を費やしましたthrow
exceptionhandlingについて話すときのコアコンセプトであるためです。, PowerShellはまた、Write-Error
を使用するすべての状況を処理するthrow
を提供しました。 したがって、これを読んだ後にthrow
を使用する必要があるとは思わないでください。
この詳細に例外処理について書く時間がかかったので、Write-Error -Stop
を使用してコードにエラーを生成することに切り替えます。 また、takeKirkのアドバイスを受けて、ThrowTerminatingError
すべての関数に対してgoto例外ハンドラを作成します。