Quando dovrei usare Write-Error vs. Throw? Errori di terminazione e non di terminazione


145

Guardando uno script Get-WebFile su PoshCode, http://poshcode.org/3226 , ho notato questo strano aggeggio a me:

$URL_Format_Error = [string]"..."
Write-Error $URL_Format_Error
return

Qual è la ragione di ciò al contrario di quanto segue?

$URL_Format_Error = [string]"..."
Throw $URL_Format_Error

O ancora meglio:

$URL_Format_Error = New-Object System.FormatException "..."
Throw $URL_Format_Error

A quanto ho capito, dovresti usare Write-Error per errori non terminanti e Throw per terminare gli errori, quindi mi sembra che non dovresti usare Write-Error seguito da Return. C'è una differenza?


4
Cosa intendi? Se Write_error consente allo script di continuare, è molto comprensibile avere una dichiarazione di ritorno dopo Write-Error. L'errore è stato scritto e si ritorna al codice che ha chiamato la funzione in primo luogo. Poiché Throw è destinato a risolvere errori, si chiuderà automaticamente in modo che una dichiarazione di ritorno in una dichiarazione di lancio sia inutile
Gisli,

1
@Gisli: E 'importante notare che returnnon non ritornare al chiamante nel processblocco di una funzione (avanzato); invece, passa all'oggetto di input successivo nella pipeline. In effetti, questo è lo scenario tipico per la generazione di errori senza interruzione: se l'elaborazione di ulteriori oggetti di input è ancora possibile.
mklement0

1
Si noti che Throwgenera una sceneggiatura errore di -terminating, che non è la stessa della dichiarazione -terminating errori innescati, per esempio, da Get-Item -NoSuchParametero 1 / 0.
mklement0

Risposte:


188

Write-Errordovrebbe essere usato se si desidera informare l'utente di un errore non critico. Per impostazione predefinita, tutto ciò che fa è stampare un messaggio di errore in testo rosso sulla console. Non impedisce il proseguimento di una pipeline o di un loop. Throwd'altra parte produce quello che viene chiamato un errore di terminazione. Se si utilizza il lancio, la pipeline e / o il loop corrente verranno chiusi. In effetti tutta l'esecuzione verrà terminata a meno che non si utilizzi una trapo una try/catchstruttura per gestire l'errore di chiusura.

C'è una cosa da notare, se si imposta $ErrorActionPreferenceper"Stop" e utilizzare Write-Erroresso produrrà un errore fatale .

Nello script a cui ti sei collegato troviamo questo:

if ($url.Contains("http")) {
       $request = [System.Net.HttpWebRequest]::Create($url)
}
else {
       $URL_Format_Error = [string]"Connection protocol not specified. Recommended action: Try again using protocol (for example 'http://" + $url + "') instead. Function aborting..."
       Write-Error $URL_Format_Error
    return
   }

Sembra che l'autore di quella funzione abbia voluto interrompere l'esecuzione di quella funzione e visualizzare un messaggio di errore sullo schermo ma non ha voluto interrompere l'esecuzione dell'intero script. L'autore dello script avrebbe potuto usare, throwtuttavia ciò significherebbe che dovresti usare un try/catchquando si chiama la funzione.

returnuscirà dall'ambito corrente che può essere una funzione, uno script o un blocco di script. Questo è meglio illustrato con il codice:

# A foreach loop.
foreach ( $i in  (1..10) ) { Write-Host $i ; if ($i -eq 5) { return } }

# A for loop.
for ($i = 1; $i -le 10; $i++) { Write-Host $i ; if ($i -eq 5) { return } }

Uscita per entrambi:

1
2
3
4
5

Un gotcha qui sta usando returncon ForEach-Object. Non interromperà l'elaborazione come ci si aspetterebbe.

Maggiori informazioni:


Ok, quindi Throw fermerà tutto, Write-Error + return interromperà solo la funzione corrente.
Bill Barry,

@BillBarry Ho aggiornato un po 'la mia risposta con una spiegazione di return.
Andy Arismendi,

Che dire di Errore di scrittura seguito da exit (1) per garantire il codice di errore appropriato restituito al sistema operativo? È mai appropriato?
pabrams,

19

La differenza principale tra il cmdlet Write-Error e la parola chiave throw in PowerShell è che la prima stampa semplicemente del testo nel flusso di errori standard (stderr) , mentre la seconda in realtà termina l'elaborazione del comando o della funzione in esecuzione, che viene quindi gestita da PowerShell inviando informazioni sull'errore alla console.

Puoi osservare il diverso comportamento dei due negli esempi forniti:

$URL_Format_Error = [string]"..."
Write-Error $URL_Format_Error
return

In questo esempio la returnparola chiave è stata aggiunta per interrompere esplicitamente l'esecuzione dello script dopo che il messaggio di errore è stato inviato alla console. Nel secondo esempio, invece, la returnparola chiave non è necessaria poiché la terminazione è implicitamente effettuata da throw:

$URL_Format_Error = New-Object System.FormatException "..."
Throw $URL_Format_Error

2
se hai $ ErrorActionPreference = "Stop", anche Write-Error termina il processo.
Michael Freidgeim,

4
Buone informazioni, ma mentre il flusso di errori di PowerShell è analogo al flusso stderr basato su testo in altre shell, come tutti i flussi di PowerShell contiene oggetti , vale a dire [System.Management.Automation.ErrorRecord]istanze, che vengono raccolte per impostazione predefinita nella $Errorraccolta automatica ( $Error[0]contiene l'errore più recente). Anche se lo usi solo Write-Errorcon una stringa , quella stringa viene racchiusa in [System.Management.Automation.ErrorRecord]un'istanza.
mklement0

16

Importante : esistono 2 tipi di errori di terminazione , che purtroppo gli argomenti della guida corrente confondono :

  • errori di terminazione delle istruzioni , come riportato dai cmdlet in determinate situazioni non recuperabili e dalle espressioni in cui si verifica un'eccezione .NET / un errore di runtime PS; viene terminatasolo l' istruzione e l' esecuzione dello script continua per impostazione predefinita .

  • errori di terminazione dello script (più precisamente: terminazione di spazio ), come innescati daThrowo aumentando uno degli altri tipi di errore tramite la variabile di preferenza / valore dell'errore-azione / valore del parametroStop.
    Se non rilevati, terminano lo spazio di esecuzione corrente (thread); cioè terminano non solo lo script corrente, ma anche tutti i suoi chiamanti, se applicabile).

Per una panoramica completa della gestione degli errori di PowerShell, vedere questo problema di documentazione di GitHub .

Il resto di questo post si concentra su non fatale vs. dichiarazione terminazione errori.


Per completare le risposte utile esistenti con un focus sul nocciolo della questione: Come si fa a scegliere se denunciare un statement- terminazione o non fatale errore ?

La segnalazione errori Cmdlet contiene linee guida utili; fammi provare un riassunto pragmatico :

L'idea generale dietro non fatale errori è quello di consentire un "fault-tolerant" elaborazione di grandi insiemi di ingresso : mancata elaborare un sottoinsieme degli oggetti di input dovrebbero (per default) abort il - potenzialmente lungo corso - processo nel suo complesso , consentendo di ispezionare gli errori e rielaborare solo gli oggetti non riusciti in un secondo momento , come riportato dai record di errore raccolti nella variabile automatica $Error.

  • Segnala un errore NON RISOLUTIVO , se il cmdlet / la funzione avanzata:

    • accetta MULTIPLE oggetti di input , tramite input di pipeline e / o parametri con valori di array, AND
    • si verificano errori per oggetti di input SPECIFICI , AND
    • questi errori NON PREVENGONO L'ELABORAZIONE DI ULTERIORI oggetti di input IN PRINCIPIO ( situazionalmente , non possono essere rimasti oggetti di input e / o oggetti di input precedenti potrebbero essere già stati elaborati correttamente).
      • Nelle funzioni avanzate, utilizzare $PSCmdlet.WriteError()per segnalare un errore non terminante ( Write-Error, sfortunatamente, non è $?possibile impostarlo $Falsenell'ambito del chiamante - vedere questo problema di GitHub ).
      • Gestione di un errore non terminante: $?indica se il comando più recente ha segnalato almeno un errore non terminante.
        • Pertanto, $?essere $Falsepuò significare che qualsiasi sottoinsieme (non vuoto) di oggetti di input non è stato elaborato correttamente, possibilmente l'intero set.
        • La variabile di preferenza $ErrorActionPreferencee / o il parametro cmdlet comune -ErrorActionpossono modificare (solo) il comportamento degli errori non terminanti in termini di comportamento dell'output degli errori e se gli errori non terminanti debbano essere convertiti in errori con terminazione di script .
  • Segnala un errore di TERMINAZIONE DI DICHIARAZIONE in tutti gli altri casi .

    • In particolare, se si verifica un errore in un cmdlet / funzione avanzata che accetta solo un oggetto di input SINGLE o NO e genera NO o un oggetto di output SINGLE o accetta solo input di parametri e i valori dei parametri forniti ne impediscono un funzionamento significativo.
      • Nelle funzioni avanzate, è necessario utilizzare $PSCmdlet.ThrowTerminatingError()per generare un errore di terminazione delle istruzioni.
      • Si noti che, al contrario, la Throwparola chiave genera un errore di terminazione dello script che interrompe l' intero script (tecnicamente: il thread corrente ).
      • Gestione di un errore che termina un'istruzione: è possibile utilizzare un try/catchgestore o trapun'istruzione (che non può essere utilizzata con errori non di terminazione ), ma si noti che anche gli errori di terminazione delle istruzioni per impostazione predefinita non impediscono l'esecuzione del resto dello script. Come per gli errori senza interruzione , $?indica $Falsese l'istruzione precedente ha generato un errore di interruzione dell'istruzione.

Purtroppo, non tutti i cmdlet principali di PowerShell sono conformi a queste regole :

  • Anche se è improbabile che fallisca, New-TemporaryFile(PSv5 +) segnalerebbe un errore non terminante se falliva, nonostante non accettasse l'input della pipeline e producesse solo un oggetto di output - questo è stato corretto almeno da PowerShell [Core] 7.0, tuttavia: vedi questo GitHub problema .

  • Resume-JobL 'aiuto afferma che passare un tipo di lavoro non supportato (come un lavoro creato con Start-Job, che non è supportato, perché Resume-Jobsi applica solo ai lavori di flusso di lavoro) provoca un errore di terminazione, ma ciò non è vero a partire da PSv5.1.


8

Write-Errorconsente al consumatore della funzione di sopprimere il messaggio di errore con -ErrorAction SilentlyContinue(in alternativa -ea 0). Mentre throwrichiede atry{...} catch {..}

Per usare un tentativo ... prendi con Write-Error:

try {
    SomeFunction -ErrorAction Stop
}
catch {
    DoSomething
}

Perché è necessario chiamare Errore di scrittura, se si desidera eliminare il messaggio di errore?
Michael Freidgeim, il

8

Aggiunta alla risposta di Andy Arismendi :

Se l' errore di scrittura termina il processo o meno dipende $ErrorActionPreferencedall'impostazione.

Per gli script non banali, $ErrorActionPreference = "Stop"è un'impostazione consigliata per il fallimento veloce.

"Il comportamento predefinito di PowerShell rispetto agli errori, che è quello di continuare in caso di errore ... sembra molto VB6" On Error Resume Next "-ish"

(da http://codebetter.com/jameskovacs/2010/02/25/the-exec-problem/ )

Tuttavia, fa Write-Errorterminare le chiamate.

Per utilizzare Write-Error come comando non terminante indipendentemente dalle altre impostazioni di ambiente, è possibile utilizzare un parametro comune -ErrorAction con valore Continue:

 Write-Error "Error Message" -ErrorAction:Continue

0

Se la tua lettura del codice è corretta, allora hai ragione. Dovrebbero essere utilizzati errori di terminazione throwe, se si ha a che fare con tipi .NET, è utile seguire anche le convenzioni di eccezione .NET.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.