PowerShellから標準エラーに書き込み、または次のようなエラーをトラップするにはどうすればよいですか。
ここ数年、私はthrow
ingエラーまたはWrite-Error
経由の書き込みで生き残ってきましたが、私は疲れていて年をとっています。スクリプトでは、簡潔なエラーメッセージを1つだけ見たいと思っています。私はtrap
、throw
、Write-Error
、および-ErrorAction
のすべての組み合わせを試して、役に立たないようにしました:
try {
throw "error" # Sample code for a stack overflow. In the theater
# of your mind, imagine there is code here that does something real and useful
} catch {
Write-Error "An error occurred attempting to 'do something.' Have you tried rebooting?"
}
ここに私が見たいユーザーエクスペリエンスがあります:
C:\> & .\Do-Something.ps1
An error occurred attempting to 'do something.' Have you tried rebooting?
C:\> ▏
代わりに私は得る:
C:\> & .\Do-Something.ps1
An error occurred attempting to 'do something.' Have you tried rebooting?
At line:1 char:1
+ Do-RealWork
+ ~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Do-RealWork
C:\> ▏
自動_$ErrorView
_変数を_'CategoryView'
_に設定すると、PowerShellは代わりに簡潔な単一行エラー表現を出力しますが、この表現には常に十分な情報が含まれない場合があります。エラーメッセージは通常ではないが含まれるため。プラス側では、_Throw "..."
_ isに渡されるテキストは反映されますが、対照的に、_Write-Error
_出力にはno特定の情報が含まれますが、_'CategoryView'
_有効です。
PowerShellに新しい行を1行で追加しますが、すべての重要な情報が常に含まれています v6について説明中 。
PowerShellコードがconsole(コンソールのホストを使用)から実行される場合、[Console]::Error.WriteLine()
を使用します無条件が外の世界のstderrに書き込みます)(標準エラーストリーム):
_[Console]::Error.WriteLine("An error occurred ... Have you tried rebooting?")
_
注意:
これは、PowerShell ISEなどの非コンソールホストからは機能しません。
[Console]::Error.WriteLine()
出力がコンソールの赤に出力されない [1]。
悲しいことに、within PowerShell(ホスト全体)and from outsideの両方から機能する単一のソリューションはありません:
[Console]::Error.WriteLine()
は、外の世界のstderrに適切に書き込んでいる間、その出力をキャプチャまたは抑制できません内部 PowerShellではなく、PowerShellでのみ機能しますコンソール ホスト。
同様に、$Host.ui.WriteErrorLine()
は、allホストで機能しますが、PowerShellのストリームシステムの外部で機能する[〜#〜] ui [〜#〜]メソッドですまた、その出力もPowerShellでキャプチャまたは抑制できません。
さらに重要なことに、それは外界のstderrに書き込みません(この点で_Write-Error
_のように動作します。以下を参照してください)。
内部 PowerShell、_Write-Error
_のみがPowerShellのエラーストリームに書き込むため、その出力できるはキャプチャ/抑制されます。
ただし、残念ながら、_Write-Error
_(ノイズが多いことを除いて)は、外部のstderrにnot書き込みます以外の場合、奇妙にstderrは明示的にリダイレクトされます-詳細は、私の この答え を参照してください。
[1]ピーター(OP自身)はそのための回避策を提供します。
_[Console]::ForegroundColor = 'red'
[Console]::Error.WriteLine("An error occurred ... Have you tried rebooting?")
[Console]::ResetColor()
_
sunegの役立つ答え は、関数ラッパーを提供します。
幸い、PowerShellは、出力が(ファイルに)リダイレクトされていることを検出すると、自動的にカラーコードを省略します。
前の回答のアイデア に基づいて、組み込みのWrite-Errorコマンドレットをカスタム関数で一時的にオーバーライドできます。
# Override the built-in cmdlet with a custom version
function Write-Error($message) {
[Console]::ForegroundColor = 'red'
[Console]::Error.WriteLine($message)
[Console]::ResetColor()
}
# Pretty-print "Something is wrong" on stderr (in red).
Write-Error "Something is wrong"
# Setting things back to normal
Remove-Item function:Write-Error
# Print the standard bloated Powershell errors
Write-Error "Back to normal errors"
これにより、Powershell関数がコマンドレットよりも優先されるという事実を利用できます。
https://technet.Microsoft.com/en-us/library/hh848304.aspx
これは、TeamCityに問題を簡単に検出させるだけでなく、美しく簡潔なエラーメッセージを表示するために私が思いついた最もエレガントなアプローチです。
私は最近この問題を自分で解決する必要があったので、ここに詳細を示すようにWrite-ErrorMessage関数をまとめました: https://intellitect.com/powershell-write-error-without-writing-stack-trace/
具体的には、私は組み合わせを活用しました
Write-Error -Message $err -ErrorAction SilentlyContinue
$Host.UI.WriteErrorLine($errorMessage)
私の意見では、PowerShellでエラーをトラップする最善の方法は、以下を使用することです。
$Error[0].Exception.GetType().FullName
これを適切に使用する方法の例を次に示します。基本的に、スクリプトが失敗するさまざまなシナリオでPowerShellで実行しようとしていることをテストします。
次に、PowerShellの一般的なエラーメッセージを示します。
PS C:\> Stop-Process -Name 'FakeProcess'
Stop-Process : Cannot find a process with the name "FakeProcess". Verify the process name and call the cmdlet again.
At line:1 char:1
+ Stop-Process -Name 'FakeProcess'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (FakeProcess:String) [Stop-Process], ProcessCommandException
+ FullyQualifiedErrorId : NoProcessFoundForGivenName,Microsoft.PowerShell.Commands.StopProcessCommand
次に、エラーメッセージの例外を取得します。
PS C:\> $Error[0].Exception.GetType().FullName
Microsoft.PowerShell.Commands.ProcessCommandException
次のように、エラーメッセージをキャッチするようにコードを設定します。
Try
{
#-ErrorAction Stop is needed to go to catch statement on error
Get-Process -Name 'FakeProcess' -ErrorAction Stop
}
Catch [Microsoft.PowerShell.Commands.ProcessCommandException]
{
Write-Host "ERROR: Process Does Not Exist. Please Check Process Name"
}
上記の例では、Powershellの標準エラーではなく、出力は次のようになります。
ERROR: Process Does Not Exist. Please Check Process Name
最後に、複数のcatchブロックを使用して、コード内の複数のエラーを処理することもできます。 「ブランケット」catchブロックを含めて、処理していないすべてのエラーをキャッチすることもできます。例:
Try
{
Get-Process -Name 'FakeProcess' -ErrorAction Stop
}
Catch [Microsoft.PowerShell.Commands.ProcessCommandException]
{
Write-Host "ERROR: Process Does Not Exist. Please Check Process Name"
}
Catch [System.Exception]
{
Write-Host "ERROR: Some Error Message Here!"
}
Catch
{
Write-Host "ERROR: I am a blanket catch to handle all unspecified errors you aren't handling yet!"
}
スネッグの答え に基づいて、書き込みエラーをカスタム関数と簡単に入れ替えることができるように、次の関数を書きました。ユーザーがPowerShell ISEから書き込みエラーを呼び出しているかどうかのチェックも追加しました
# Override the built-in cmdlet with a custom version
function New-ErrorFunc {
function Dyn($message){
param($message,$ErrorAction)
if($psISE){
$Host.UI.WriteErrorLine($message)
}
else{
[Console]::ForegroundColor = 'red'
[Console]::Error.WriteLine($message)
[Console]::ResetColor()
}
if($ErrorAction -eq 'Stop'){
Break
}
}
return ${function:Dyn}
}
function Set-ErrorFunc(){
param([bool]$custom=$true)
if($custom){
$dynfex= New-ErrorFunc
Invoke-Expression -Command "function script:Write-Error{ $dynfex }"
}
else {
$custom= Get-Command Write-Error | Where-Object {$_.CommandType -eq 'Function'}
if($custom){ Remove-Item function:Write-Error }
}
}
#User our Custom Error Function
Set-ErrorFunc
# Pretty-print "Something is wrong" on stderr (in red).
Write-Error "Something is wrong"
# Setting things back to normal
Set-ErrorFunc -custom $false
# Print the standard bloated Powershell errors
Write-Error "Back to normal errors"