When should I use Write-Error vs. Throw? Terminating vs. non-terminating errors

前端 未结 6 1668
别跟我提以往
别跟我提以往 2020-11-28 03:55

Looking at a Get-WebFile script over on PoshCode, http://poshcode.org/3226, I noticed this strange-to-me contraption:

$URL_Format_Error = [string]\"...\"
Wri         


        
6条回答
  •  孤城傲影
    2020-11-28 04:25

    Important: There are 2 types of terminating errors, which the current help topics unfortunately conflate:

    • statement-terminating errors, as reported by cmdlets in certain non-recoverable situations and by expressions in which a .NET exception / a PS runtime error occurs; only the statement is terminated, and script execution continues by default.

    • script-terminating errors (more accurately: runspace-terminating), as either triggered by Throw or by escalating one of the other error types via error-action preference-variable / parameter value Stop.
      Unless caught, they terminate the current runspace (thread); that is, they terminate not just the current script, but all its callers too, if applicable).

    For a comprehensive overview of PowerShell's error handling, see this GitHub documentation issue.

    The remainder of this post focuses on non-terminating vs. statement-terminating errors.


    To complement the existing helpful answers with a focus on the core of the question: How do you choose whether to report a statement-terminating or non-terminating error?

    Cmdlet Error Reporting contains helpful guidelines; let me attempt a pragmatic summary:

    The general idea behind non-terminating errors is to allow "fault-tolerant" processing of large input sets: failure to process a subset of the input objects should not (by default) abort the - potentially long-running - process as a whole, allowing you to inspect the errors and reprocess only the failed objects later - as reported via the error records collected in automatic variable $Error.

    • Report a NON-TERMINATING error, if your cmdlet / advanced function:

      • accepts MULTIPLE input objects, via the pipeline input and/or array-valued parameters, AND
      • errors occur for SPECIFIC input objects, AND
      • these errors DO NOT PREVENT PROCESSING of FURTHER input objects IN PRINCIPLE (situationally, there may be no input objects left and/or previous input objects may already have been processed successfully).
        • In advanced functions, use $PSCmdlet.WriteError() to report a non-terminating error (Write-Error, unfortunately, doesn't cause $? to be set to $False in the caller's scope - see this GitHub issue).
        • Handling a non-terminating error: $? tells you whether the most recent command reported at least one non-terminating error.
          • Thus, $? being $False can either mean that any (nonempty) subset of input objects weren't properly processed, possibly the entire set.
          • Preference variable $ErrorActionPreference and/or common cmdlet parameter -ErrorAction can modify the behavior of non-terminating errors (only) in terms of error output behavior and whether non-terminating errors should be escalated to script-terminating ones.
    • Report a STATEMENT-TERMINATING error in all other cases.

      • Notably, if an error occurs in a cmdlet / advanced function that only accepts a SINGLE or NO input object and outputs NO or a SINGLE output object or takes parameter input only and the parameter values given prevent meaningful operation.
        • In advanced functions, you must use $PSCmdlet.ThrowTerminatingError() in order to generate a statement-terminating error.
        • Note that, by contrast, the Throw keyword generates a script-terminating error that aborts the entire script (technically: the current thread).
        • Handling a statement-terminating error: A try/catch handler or trap statement may be used (which cannot be used with non-terminating errors), but note that even statement-terminating errors by default do not prevent the rest of the script from running. As with non-terminating errors, $? reflects $False if the previous statement triggered a statement-terminating error.

    Sadly, not all of PowerShell's own core cmdlets play by these rules:

    • While unlikely to fail, New-TemporaryFile (PSv5+) would report a non-terminating error if it failed, despite not accepting pipeline input and only producing one output object - this has been corrected as of at least PowerShell [Core] 7.0, however: see this GitHub issue.

    • Resume-Job 's help claims that passing an unsupported job type (such as a job created with Start-Job, which is not supported, because Resume-Job only applies to workflow jobs) causes a terminating error, but that's not true as of PSv5.1.

提交回复
热议问题