Welcome to Our Website

例外について知りたかったすべて

  • 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"}

これにより、終了エラーである実行時例外が作成されます。 それはによって処理されますcatchacalling関数内またはこのようなメッセージでスクリプトを終了します。

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です。

ここで重要な詳細は、コードのScriptNameLine、および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例外は他の例外から継承できることを認識することが重要です。 上記の例では、FileNotFoundExceptionIOExceptionを継承しています。 したがって、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に追加することができますが、catchexceptiontypeによるエラー。 使用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それをしないでください。, あなたはcatchithappens時に何らかのアクションを処理または実行する予定の例外のみを使用する必要があります。

例外に対してアクションを実行したいが、下流のsosomethingがそれを処理できる例外を再スローする場合があります。 このメッセージを書き込むにはログの問題でありwediscoverでも取扱いの一層のスタックです。

catch{ Write-Log $PSItem.ToString() throw $PSItem}

興味深いことに、throwcatch内から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があり、その行が問題のある場所であることを示していることです。

エラーメッセージが表示されると、throw31行目にthrow

Dexter Dhamiは、ThrowTerminatingError()を使用して修正できることを指摘しました。,

ThrowTerminatingError()Get-Resourceという関数内で呼び出されたと仮定すると、これは表示されるエラーです。

問題の原因としてGet-Resource関数をどのように指しているかわかりますか? えtheuser何かと便利です。

$PSItemErrorRecordであるため、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{...}すべてのbeginprocessおよびend彼のすべてのadvancedfunctionsにブロックを配置することでした。 これらの一般的なcatchブロックでは、$PSCmdlet.ThrowTerminatingError($PSItem)を使用して、関数を残すすべての例外を処理する単一の行があります。

function Start-Something{ param() process { try { ... } catch { $PSCmdlet.ThrowTerminatingError($PSItem) } }}

すべてがtry関数内のステートメントにあるため、すべてが一貫して機能します。, Thisalsoをクリーンミラーをエンドユーザーにと皮内部コードから発生するエラーになります。

トラップ

私はtry/catch例外の側面に焦点を当てました。 しかし、これをまとめる前に、私が言及する必要があるレガシー機能があります。

trapは、そのスコープで発生するすべての例外をキャッチするためのスクリプトまたは関数に配置されます。 例外が発生すると、trapのコードが実行され、通常のコードが続行されます。 複数の例外が発生した場合、トラップは何度も呼び出されます。,私は個人的にこのアプローチを採用したことはありませんが、すべての例外をログに記録するadminまたはcontrollerスクリプトの値を見ることができます。

閉会の挨拶

スクリプトに適切な例外処理を追加すると、スクリプトがより安定しているだけでなく、これらの例外をトラブルシューティングするのが容易になります。

私は多くの時間を費やしましたthrowexceptionhandlingについて話すときのコアコンセプトであるためです。, PowerShellはまた、Write-Errorを使用するすべての状況を処理するthrowを提供しました。 したがって、これを読んだ後にthrowを使用する必要があるとは思わないでください。

この詳細に例外処理について書く時間がかかったので、Write-Error -Stopを使用してコードにエラーを生成することに切り替えます。 また、takeKirkのアドバイスを受けて、ThrowTerminatingErrorすべての関数に対してgoto例外ハンドラを作成します。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です