2011-06-21 28 views
7

スクリプト関数でエラーが発生したかどうかを検出する最適な方法は何ですか?私は、$に似たエラー/成功ステータスを示す一貫した方法を探していますか? (コマンドレットでのみ機能し、スクリプト関数では機能しません)。PowerShell:スクリプト関数のエラーを検出する

特定の関数が呼び出し元によって使用される値を返す場合、ブール値を返すことで成功を示すことはできません。関数は[ref]パラメータを使用して関数内で適切に値を設定し、呼び出し後にチェックすることができますが、これは私が望む以上にオーバーヘッドです。 PowerShellに組み込みのものがありますか?

私が思い付くことができる最高は次のとおりです。 に機能使用書き込みエラーで

  1. はエラー ストリームにErrorRecordオブジェクトを置きます。
  2. ErrorVariableパラメータを使用して関数を呼び出します。
  3. ErrorVariableパラメータ がコール後にnullでないかどうかを確認します。たとえば、

function MyFun { 
    [CmdletBinding()] # must be an advanced function or this 
    param()    # will not work as ErrorVariable will not exist 
    process { 
    # code here.... 
    if ($SomeErrorCondition) { 
     Write-Error -Message "Error occurred; details..." 
     return 
    } 
    # more code here.... 
    } 
} 

# call the function 
$Err = $null 
MyFun -ErrorVariable Err 
# this check would be similar to checking if $? -eq $false 
if ($Err -ne $null) { 
    "An error was detected" 
    # handle error, log/email contents of $Err, etc. 
} 

はもっと良いものがありますか? $を使う方法はありますか?私たちのスクリプト関数では?私はむしろ例外やErrorRecordオブジェクトを投げたり、たくさんのtry/catchブロックを置いています。私はむしろ呼び出しの前にそこに他のエラーがある可能性があるので、関数呼び出しを行う前にカウントをチェックする必要があるので、$ Errorを使用しないでください。Clear()して失うことは望ましくありません。

+0

なぜ 'try/catch'を使いたくないのですか?それは 'if'文をはるかに上回るものではありません。 'try {} catch {}'と 'if($ err){}'は2文字の違いだけです。 – Rynant

+0

私がしたいのは: 'foo -ev Err' '($?-eq $ false){ReportError $ Err} ' try/catchのすべての関数呼び出しをラッピングすると、 'if(...){ReportError ...}'を使ったすべての関数呼び出しの後には、より洗練されたようです。私はtry/catchでコードをラップする方が好きです。本当にcatch/catchできない場合はエラーを防ぐことができます。 – DanW

+0

'dir nodrivefound:\ 'を考えてください ErrorRecordsはエラーストリームに入れられ、2>または-ErrorVariable、$? $ falseであるため、エラーは簡単に検出されますが、スクリプトは終了せず、try/catchもありません。私はそのようなことを望んでいた。 – DanW

答えて

3

2つのことが心に来る:Throw(上記のあなたの例ではWrite-Errorよりも良い)、およびtry..catch

try 
{ 
    #code here 
} 
catch 
{ 
    if ($error[0].Exception -match "some particular error") 
    { 
     Write-Error "Oh No! You did it!" 
    } 
    else 
    { 
     Throw ("Ooops! " + $error[0].Exception) 
    } 
} 

私見、可能な限りそのエラーを処理する関数自体を持っていることが一般的に優れています。

+0

(最後のコメントは無視してください) 可能であれば、例外を投げたりtry/catchブロックを避けたいと思います。理想的には、関数はコマンドレットと同様に動作する必要があります。処理/フィルタリング/無視されるエラーストリームにErrorRecordsを追加するなどです。しかし、多くの異なる方法で、発信者が決定します。例外をスローすることは、明示的にキャッチされない限り、現在の関数とスクリプトの実行を終了するので厳しいようです。私は$のシンプルなエレガンスを好む?... – DanW

+0

コマンドレットが例外をスローすると、エラーストリームに終わります。だから、例外を投げることはCmdletと同じように動作します。例外は、失敗したコマンドを通知する適切な方法です。 – JasonMArcher

+0

例外を再現する場合は、 'throw $ _'を使ってスタックトレースやその他の貴重な情報を食べないようにしてください。 – JasonMArcher

0

$?関数が終了エラーをスローするかどうかによって異なります。 Write-Errorが使用された場合、Throwではなく、$?設定されていません。多くのコマンドレットは$を設定しません。エラーが発生した場合は、そのエラーは終了エラーではないためです。

機能を設定する最も簡単な方法は$ですか? -ErrorAction Stopを使用することです。これはあなたの関数のエラーと$?設定されます。

このサンプルブロックにはどのように$が表示されていますか?作品:変数名:

function foo([ParameteR()]$p) { Write-Error "problem" } 

foo 

$? 

foo -errorAction Stop 



$? 

function foo() { throw "problem" } 

foo 

$? 

希望これは、私はあなたがグローバル変数$ GLOBALをしたいと考えてい

+0

-ErrorAction停止してスローします - 彼らは$を設定しますか? $ falseに - しかし$?あなたの例では、-ErrorActionの停止またはスローのためにスクリプトが終了すると、それ以降は表示されません。 fooの呼び出しがtry/catchでラップされている場合は動作しますが、try/catchですべての関数呼び出しをラップする必要はありません。 try/catchを使用している場合、$をチェックすることはできません。 catchブロックにいるとエラーが発生したためです。私がやりたいのは何 – DanW

+0

は以下の通りです - それはきれいだが、すべてのエラーを処理します。 'FOO -ErrorVariable Err' 'もし($ -eq $ falseの?){でReportError $ Errは} 'あなたはまた、 渡すことができます機能をReportErrorに名前とパラメータを簡単に(スプラットを使用する場合)追加します。 – DanW

0

に役立ちます。その変数は、関数だけでなくスクリプトのスコープ内にあります。

コードを見ると、トラップ(Get-Help about_Trap)も使用できますが、$ GLOBAL:variable_nameはあなたの上で動作しますが。ここにコード例のre-wreiteがあります - 私はこれをテストしていないので、より擬似コードです...:)

function MyFun { 
    [CmdletBinding()] # must be an advanced function or this 
    param()    # will not work as ErrorVariable will not exist 
    begin { 
    trap { 
     $GLOBAL:my_error_boolean = $true 
     $GLOBAL:my_error_message = "Error Message?" 

     return 
    } 
    } 
    process { 
    # more code here.... 
    } 
} 

# call the function 
$GLOBAL:my_error_boolean = $false 
MyFun 
# this check would be similar to checking if $? -eq $false 
if ($GLOBAL:my_error_boolean) { 
    "An error was detected" 
    # handle error, log/email contents of $Err, etc. 
} 

HTH、マット・

6

スクリプトから呼び出されたコマンドで発生したエラーをログに記録する一般的な機構を探しているようですね。次の出力は、呼び出された関数への変更を必要とせず、

Set-Alias ReportError Write-Host -Scope script # placeholder for actual logging 

trap { 
    ReportError @" 
Error in script $($_.InvocationInfo.ScriptName) : 
$($_.Exception) $($_.InvocationInfo.PositionMessage) 
"@ 
    continue # or use 'break' to stop script execution 
} 

function f([int]$a, [switch]$err) { 
    "begin $a" 
    if($err) { throw 'err' } 
    " end $a" 
} 

f 1 
f 2 -err 
f 3 

このテストスクリプトを実行する生成します:もしそうなら、trapは、おそらく最も適切なメカニズムであるスクリプトの実行は、後に停止すべき

PS> ./test.ps1 
begin 1 
    end 1 
begin 2 
Error in script C:\Users\umami\t.ps1 : 
System.Management.Automation.RuntimeException: err 
At C:\Users\umami\t.ps1:13 char:21 
+ if($err) { throw <<<< 'err' } 
begin 3 
    end 3 

場合エラーが報告された場合は、continuebreakに置き換えてください。

+0

そこに続行すると、例外情報と 'begin 3'の行の間に' end 2'という行がありませんか?そうでない場合は、どうしてですか? –

+0

@CodeJockey 'trap'は関数内ではなくスクリプト内にあるため、関数が終了した後に外側のスクリプトで' continue'が実行されます。 'trap'ブロックを関数に移動すると、記述した動作が得られます。 –

11

スクリプト機能でエラーが発生したかどうかを検出する最適な方法は何ですか?私は、$に似たエラー/成功ステータスを示す一貫した方法を探していますか? (コマンドレットでのみ機能し、スクリプト関数では機能しません)。

PowerShellでのエラー処理は完全に混乱します。エラーレコード、スクリプト例外、.NET例外、$?$LASTEXITCODEtrap$Error配列(スコープ間)などがあります。これらの要素を相互にやりとりするための構造体($ErrorActionPreferenceなど)。このような泥棒があるときに一貫性を持たせることは非常に困難です。しかし、この目標を達成する方法があります。

以下の観察がなされなければならない。

  • $?はunderdocumented謎です。 $?コマンドレット呼び出しの値は伝播しません。これは "読み取り専用変数"です(したがって手動で設定することはできません)。が正確にになったときにはクリアされません( "実行ステータス" about_Automatic_Variables$?の説明を除いてPowerShellで使用されていない用語は、謎です)。ありがたいことに、Bruce Payetteがこれを明らかにしました。$?を設定したい場合は、$PSCmdlet.WriteError()しか知られていません。

  • コマンドレットとして機能に$?を設定する場合は、Write-Errorを控え、代わりに$PSCmdlet.WriteError()を使用する必要があります。 Write-Error$PSCmdlet.WriteError()は同じことをしますが、前者は$?を正しく設定せず、後者は行います。

  • .NET例外を適切に処理したい場合(非終了エラーの場合と同じように、実行全体を停止するという決定を残しておきます)クライアントコード)は、catch$PSCmdlet.WriteError()でなければなりません。 ではなく、のというエラーを終了しないので、未処理のままにすることはできません。 (どちらかの文書化されていません。)つまり

、一貫性のあるエラー処理の動作を生成するための鍵は、可能な限り$PSCmdlet.WriteError()を使用することです。$?を設定し、$ErrorActionPreference(したがって-ErrorAction)を受け取り、他のコマンドレットから生成されたSystem.Management.Automation.ErrorRecordオブジェクトまたはcatchステートメント($_変数)を受け入れます。

次の例では、このメソッドの使用方法を示します。最後の注意点としては

# Function which propagates an error from an internal cmdlet call, 
# setting $? in the process. 
function F1 { 
    [CmdletBinding()] 
    param([String[]]$Path) 

    # Run some cmdlet that might fail, quieting any error output. 
    Convert-Path -Path:$Path -ErrorAction:SilentlyContinue 
    if (-not $?) { 
     # Re-issue the last error in case of failure. This sets $?. 
     # Note that the Global scope must be explicitly selected if the function is inside 
     # a module. Selecting it otherwise also does not hurt. 
     $PSCmdlet.WriteError($Global:Error[0]) 
     return 
    } 

    # Additional processing. 
    # ... 
} 


# Function which converts a .NET exception in a non-terminating error, 
# respecting both $? and $ErrorPreference. 
function F2 { 
    [CmdletBinding()] 
    param() 

    try { 
     [DateTime]"" # Throws a RuntimeException. 
    } 
    catch { 
     # Write out the error record produced from the .NET exception. 
     $PSCmdlet.WriteError($_) 
     return 
    } 
} 

# Function which issues an arbitrary error. 
function F3 { 
    [CmdletBinding()] 
    param() 

    # Creates a new error record and writes it out. 
    $PSCmdlet.WriteError((New-Object -TypeName:"Management.Automation.ErrorRecord" 
     -ArgumentList:@(
      [Exception]"Some error happened", 
      $null, 
      [Management.Automation.ErrorCategory]::NotSpecified, 
      $null 
     ) 
    )) 

    # The cmdlet error propagation technique using Write-Error also works. 
    Write-Error -Message:"Some error happened" -Category:NotSpecified -ErrorAction:SilentlyContinue 
    $PSCmdlet.WriteError($Global:Error[0]) 
} 

、あなたは.NETの例外から終了エラーを作成する場合は、try/catch行うと例外がキャッチさthrow再。

+0

@alekの解決策を得ることができませんでした。 -ErrorActionを設定する:SilentlyContinueはエラーを無視します。 MSがPS 5.1で何か変わったからでしょうか? – woter324

0

それは右の私の頭の上に行ってきましたように、こののほとんどは素敵なシューという音を立て...ಠ_ಠ

私はダンと思います。 PSロギングは完全な混乱で、私が書いているコードのサイズを2倍以上にするようです...

コンソール出力をログ、疣贅、およびすべてに直接キャプチャできればとってもうれしいです。 ..

Try/Catchブロックはそうです...だからこそ、私はそれを嗅ぐことができ、私の目は茶色に変わりました。

$?非常に興味深いですが、実際にあなたがしていることを知っています。どこにいても私は何も知りませんでした(先週私は少なくとも何かを知っていると思っていましたが、noooooo)。 CLIで

% $#する@%$は2のようなものが存在しないのはなぜ> ...ので、ここで

[OK]をあなたがここまで読んだ(私がしようとしているものですので、なぜ):

Function MyFunc($Param1, $Param2){ 
Do{ 
    $Var = Get-Something | Select Name, MachineName, Status 
$NotherVar = Read-Host -Prompt "Do you want to Stop or Start or check the $Var (1 to Start, 2 to stop, 3 to check, 4 to continue)?" 
    If ($SetState -eq 1) 
    { 
     Do Stuff 
    } 
    ElseIf ($Var -eq 2) 
     { 
     Do Stuff 
    } 
    ElseIf ($Var -eq 3) 
     { 
     Do Stuff 
    } 
    } 
    Until ($Var -eq 4) 
Do other stuff 
} 

それは機能しましたか?はい、いいです...ログに記録して続行してください。いいえ?そして、ところで...私はちょうど、ユーザーの入力を要求し、コンテンツを追加して続行するように誘惑しています...

を、エラーをキャッチし、それをログインしてスクリプトを続け、私は思わモジュールPSLoggingを見つけましたそれはかなりクールだろうが、私はそれを働かせる方法がわからない...ドキュメントは少しスパルタです。あなたの人のように思えるようになっているので、あまりにも手間がかからないので、私はちょっとした人のような感じです。

関連する問題