11

エラーが発生した場合にスクリプトを停止し、try... catch を使用してエラーを処理する簡単な方法を提供しようとしています。私が考えていた世界で最も簡単なことですが、私は明らかに愚かなことをしています。私は数時間読んだのですが、立ち往生しています。助けがあればとても便利です、ありがとう!

ここにいくつかのサンプルコードがあります。エラーアクションをあちこちに配置しましたが、いまいましいことを止めることはできません!

$ErrorActionPreference = "Stop"
try {
    get-content "c:\GarbageFileName.txt" -ErrorAction stop
}
catch {
    write-output "in catch, I want it to stop now"
}
write-output "try-catch finished, script is continuing"

このテキストは翌日追加されました* 人々からのすばらしい回答、複数の回答を選択できるか、しぶしぶ回答として選択しなかった回答に投票するのに十分な評判があればいいのにと思います。

4

3 に答える 3

18

try/catchコントロールの全体的な考え方は、エラーをスローしてスクリプトを停止するという既定のアクションではなく、終了エラーが発生した場合にスクリプトに何をすべきかを指示することです。catch ブロックがWrite-Hostを使用して端末にメッセージを表示するだけの場合、それがすべてのエラー処理であり、スクリプトはそこから続行されます。考えてみれば、エラーがキャッチされるたびにスクリプトが自動的に停止されると、 try/catchの目的が部分的に無効になります。

catchブロックでは、$ _は、 tryブロックからの終了エラーを表す ErrorRecord オブジェクトに設定されます( $error[0]に格納されるものと同じもの)。したがって、スクリプトを終了する最も簡単な方法は、 try/catchを使用していなかった場合にスローされるエラーを再スローすることです。

try {
  Get-Content "c:\GarbageFileName.txt" -ErrorAction stop
} catch {
  # Custom action to perform before terminating
  throw $_
}

または、デフォルトの ErrorRecord の代わりにカスタム メッセージを表示する場合:

try {
  Get-Content "c:\GarbageFileName.txt" -ErrorAction stop
} catch {
  throw 'Custom error message'
}

または、エラーストリームにエラーをスローせずにカスタムエラー処理を終了した後に終了する場合は、Joost の回答で提案されているようにbreakを使用できます。

または、より洗練された独自の ErrorRecord オブジェクトを作成することもできます。ここで包括的にカバーするには大きすぎるトピックですが、 System.Management.Automation.ErrorRecordをグーグルで検索すると、構文に関する詳細情報を取得できます。これは、開始するためのスクリプトの 1 つからの例です ( SQL Server データベースに対して$query変数で定義された SQL クエリを実行する関数から)。

} catch {
  $ErrorRecord = New-Object System.Management.Automation.ErrorRecord(
    (New-Object Exception("Exception executing the query: $($_.Exception.InnerException.Message)")),
    $query,
    [System.Management.Automation.ErrorCategory]::InvalidArgument,
    $null
  )
  $ErrorRecord.CategoryInfo.Reason = $_.CategoryInfo.Reason;
  $ErrorRecord.CategoryInfo.Activity = $_.InvocationInfo.InvocationName;
  $PSCmdlet.ThrowTerminatingError($ErrorRecord);
}

いくつかのメモ:

  • カスタム ErrorRecord を作成する際に$_を使用していることがわかります。これには、 tryブロックでキャッチされた終了エラーに関連付けられた ErrorRecord オブジェクトが含まれていると述べました。カスタム ErrorRecord の対応するプロパティに割り当てて、デフォルトの ErrorRecord の一部を使用しながら、一部のエラー出力をカスタマイズするという考え方です。
  • $PSCmdlet[CmdletBinding()]は、関数またはスクリプトの先頭で宣言した場合にのみ使用できます。それ以外の場合は、throw $ErrorRecordカスタム エラーをスローするために使用できます。ただし、 $PSCmdlet.ThrowTerminatingErrorを使用すると、結果はよりコマンドレット スタイルになります。( throwは、エラーを生成した関数からの行を吐き出しますが、$PSCmdlet.ThrowTerminatingErrorは、関数が使用された呼び出しコンテキストからの行を提供します。複雑になりすぎずに意味のある方法で説明するのは困難です。しかし、実験してみれば、私の言いたいことがわかるでしょう。)

$ErrorActionPreference = "Stop"ところで、を設定してから使用するのは冗長-ErrorAction Stopです。設定変数はすべてのコマンドレットの既定のアクションを設定し、-ErrorActionスイッチは特定のコマンドレットの既定のアクションをオーバーライドするため、最初に既定を指定してから-ErrorActionを使用して、既定として設定した同じアクションを指定する必要はありません。 . おそらくやりたいことは、ただ除外すること$ErrorActionPreference = "Stop"です。

于 2013-10-10T01:06:03.020 に答える
6

欠けているのはbreak、Catch ブロックのステートメントだけです。指示しない限り、Powershell はスクリプトを停止しません。

try {
    get-content "c:\GarbageFileName.txt" -ErrorAction stop
}
catch {
    write-output "in catch, I want it to stop now"
    break
}
write-output "try-catch finished, script is continuing"

それが役立つ場合に備えて、小さな補遺: を使用するとfinally、例外がスローされたかどうかに関係なく、常に実行されるコード行を追加できます。

try {
    get-content "c:\GarbageFileName.txt" -ErrorAction stop
}
catch {
    write-output "in catch, I want it to stop now"
    break
}
finally {
    #do some stuff here that is executed even after the break-statement, for example:
    Set-Content -Path "f:\GarbageFileName.txt" -Value $null 
}

#the line below is executed only if the error didn't happen
write-output "try-catch finished, script is continuing"
于 2013-10-09T22:10:02.447 に答える
2

Try-Catch は例外をキャッチし、それを処理できるようにします。おそらく、それを処理することは実行を停止することを意味します...しかし、暗黙的にそれを行うことはありません。再スローしない限り、実際には例外を消費します。-ErrorAction stopしかし、問題はそれよりも単純です。try ブロックは、get-content コマンドレットよりも優先されます。したがって、catch ブロックにはエラー処理がないため、実行を停止する代わりに、Catchブロックに移動して続行します。

スクリプトから try-catch ロジックを削除してみて、コマンドレットでエラーが発生するようにします。

get-content "c:\GarbageFileName.txt" -ErrorAction stop
write-output "You won't reach me if GarbageFileName doesn't exist."

そして、実行が到達しないという望ましい結果が得られるはずですwrite-output:

PS C:\> .\so-test.ps1
Get-Content : Cannot find path 'C:\GarbageFileName.txt' because it does not exist.
At C:\so-test.ps1:2 char:12
+ get-content <<<<  "c:\GarbageFileName.txt" -ErrorAction stop
    + CategoryInfo          : ObjectNotFound: (C:\GarbageFileName.txt:String) [Get-Content], ItemNotFoundException
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand
于 2013-10-09T21:30:30.843 に答える