Când ar trebui să folosesc Write-Error vs. Throw? Erori care se termină vs. erori care nu se termină (Programare, Powershell, Gestionarea Erorilor, Powershell 2.0)

Bill Barry a intrebat.

Mă uit la un script Get-WebFile pe PoshCode, http://poshcode.org/3226am observat această ciudățenie pentru mine:

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

Care este motivul pentru aceasta, spre deosebire de următoarele?

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

Sau chiar mai bine:

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

Din câte am înțeles, ar trebui să folosiți Write-Error pentru erori care nu se termină, și Throw pentru erori care se termină, așa că mi se pare că nu ar trebui să folosiți Write-Error urmat de Return. Există vreo diferență?

Comentarii

  • La ce vă referiți? Dacă Write_error permite scriptului să continue, este foarte ușor de înțeles să existe o instrucțiune return după Write-Error. Eroarea a fost scrisă și vă întoarceți la codul care a apelat funcția în primul rând. Deoarece Throw este pentru terminarea erorilor, aceasta se va termina automat, astfel încât o declarație return într-o declarație throw este inutilă.  > Por Gisli.
  • @Gisli: Este important să rețineți că return face nu se întoarce la apelant în declarația process al unei funcții (avansate); în schimb, se trece la blocul următorul obiect de intrare din cadrul canalului. Într-adevăr, acesta este scenariul tipic pentru generarea erorilor de nefinalizare: dacă este încă posibilă procesarea altor obiecte de intrare. –  > Por mklement0.
  • Rețineți că Throw generează un script-de terminare, care nu este același lucru cu cel de la declarație-terminating errors declanșată, de exemplu, de către Get-Item -NoSuchParameter sau 1 / 0. –  > Por mklement0.
6 răspunsuri
Andy Arismendi

Write-Error ar trebui să fie folosit dacă doriți să informați utilizatorul despre o eroare necritică. În mod implicit, tot ce face este să tipărească un mesaj de eroare în text roșu pe consolă. Nu oprește un pipeline sau o buclă să continue. Throw pe de altă parte, produce ceea ce se numește o eroare de terminare. Dacă utilizați throw, conducta și/sau bucla curentă vor fi terminate. De fapt, toată execuția va fi încheiată, cu excepția cazului în care utilizați o eroare trap sau a try/catch pentru a gestiona eroarea de terminare.

Trebuie să rețineți un lucru: dacă setați $ErrorActionPreference la "Stop" și utilizați Write-Error se va produce o eroare de terminare.

În scriptul pe care l-ați accesat găsim următoarele:

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
   }

Se pare că autorul acelei funcții a dorit să oprească execuția acelei funcții și să afișeze un mesaj de eroare pe ecran, dar nu a dorit ca întregul script să se oprească din execuție. Autorul scriptului ar fi putut folosi throw însă asta ar însemna că ar trebui să folosească un try/catch la apelarea funcției.

return va ieși din domeniul de aplicare curent, care poate fi o funcție, un script sau un bloc de script. Acest lucru este cel mai bine ilustrat cu cod:

# 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 } }

Ieșire pentru ambele:

1
2
3
4
5

O problemă aici este utilizarea return cu ForEach-Object. Aceasta nu va întrerupe procesarea așa cum ne-am putea aștepta.

Mai multe informații:

Comentarii

  • Ok, deci Throw va opri totul, Write-Error + return va opri doar funcția curentă. –  > Por Bill Barry.
  • @BillBarry Mi-am actualizat puțin răspunsul cu o explicație despre return. –  > Por Andy Arismendi.
  • Ce ziceți de Write-Error urmat de exit(1) pentru a asigura codul de eroare corespunzător returnat către sistemul de operare? Este acest lucru vreodată adecvat? –  > Por pabrams.
Enrico Campidoglio

Principala diferență între Write-Error și cmdlet-ul throw din PowerShell este că primul este pur și simplu tipărește un text în fluxul de eroare standard (stderr), în timp ce al doilea este de fapt termină procesarea comenzii sau a funcției în curs de execuție, care este apoi gestionată de PowerShell prin trimiterea de informații despre eroare către consolă.

Puteți observa comportamentul diferit al celor două în exemplele pe care le-ați furnizat:

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

În acest exemplu, comanda return cuvântul cheie a fost adăugat la în mod explicit să oprească în mod explicit execuția scriptului după ce mesajul de eroare a fost trimis la consolă. În al doilea exemplu, pe de altă parte, în schimb, cuvântul return nu este necesar, deoarece terminarea este realizată implicit de către throw:

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

Comentarii

  • dacă aveți $ErrorActionPreference = „Stop”, Write-Error va încheia, de asemenea, procesul. –  > Por Michael Freidgeim.
  • Bună informație, dar în timp ce fluxul de erori al PowerShell este analogic la pe bază de text stderr stream din alte shell-uri, ca toate fluxurile PowerShell, acesta conține obiecte, și anume [System.Management.Automation.ErrorRecord] instanțe, care sunt colectate în mod implicit în fluxul automat $Error automată ($Error[0] conține cea mai recentă eroare). Chiar dacă utilizați doar Write-Error cu un șir de caractere, șirul respectiv este inclus într-un [System.Management.Automation.ErrorRecord] instanță. –  > Por mklement0.
mklement0

Important: Există 2 tipuri de erori de terminare, pe care subiectele de ajutor actuale le prezintă din păcate se confundă:

  • declarație-terminare erori, raportate de cmdlet-uri în anumite situații nerecuperabile și de expresii în care apare o excepție .NET / o eroare de execuție PS; numai declarație este terminată, iar execuția scriptului continuă în mod implicit.

  • script-terminare erori (mai exact: runspace-terminarea), fie că sunt declanșate de Throw fie de escaladarea unul dintre celelalte tipuri de erori prin intermediul funcției comună -ErrorAction parametru, -ErrorAction Stop, sau prin intermediul $ErrorActionPreference variabila de preferință, $ErrorActionPreference = 'Stop'.
    Dacă nu sunt prinse, ele pun capăt spațiului de execuție curent (thread); adică, ele pun capăt nu numai scriptului curent, ci și tuturor apelanților acestuia, dacă este cazul).

Pentru o prezentare completă a gestionării erorilor în PowerShell, consultați această problemă de documentație GitHub.

Restul acestei postări se concentrează pe nefinalizarea vs. terminarea declarației erori.


Pentru a completa răspunsurile utile existente cu un accent pe miezul întrebării: Cum se face alegeți dacă să raportați o declarație-care pune capăt sau fără reziliere eroare?

Raportarea erorilor Cmdlet conține orientări utile; permiteți-mi să încerc o un rezumat pragmatic:

Ideea generală din spatele neterminarea este de a permite procesarea „tolerantă la erori” a unor seturi mari de date de intrare.: eșecul de a procesa un subset din obiectele de intrare nu ar trebui (în mod implicit) să întrerupă procesul – care poate fi de lungă durată. în ansamblul său, permițându-vă să inspectați erorile și să să prelucrați din nou numai eșuate obiecte eșuate – așa cum sunt raportate prin intermediul înregistrărilor de erori colectate în variabila automată $Error.

  • Raportați un CARE NU SE TERMINĂ eroare, în cazul în care cmdlet-ul dvs. / funcția avansată:

    • acceptă mai multe obiecte de intrare MULTIPLE, prin intermediul parametrilor de intrare în conductă și/sau a parametrilor cu valoare de matrice, ȘI
    • apar erori pentru obiecte de intrare SPECIFICEȘI
    • aceste erori NU IMPEDESC PROCESAREA în principiu a altor obiecte de intrare. (din punct de vedere situațional, este posibil să nu mai existe obiecte de intrare și/sau obiectele de intrare anterioare să fi fost deja procesate cu succes).
      • În funcțiile avansate, utilizați $PSCmdlet.WriteError() pentru a raporta o eroare care nu se termină (Write-Error, din păcate, nu provoacă $? să fie setat la $False în apelantului de către apelant – a se vedea această problemă GitHub).
      • Gestionarea unei erori care nu se termină: $? vă spune dacă cea mai recentă comandă a raportat cel puțin un eroare fără sfârșit.
        • Astfel, $? fiind $False poate însemna fie că orice subset (nevid) de obiecte de intrare nu au fost procesate corespunzător, eventual întregul set.
        • Variabila de preferință $ErrorActionPreference și/sau parametru comun al cmdletului -ErrorAction pot modifica comportamentul erorilor care nu se termină (numai) în ceea ce privește comportamentul de ieșire a erorilor și dacă erorile care nu se termină trebuie să fie escaladate la script-terminante.
  • Report a DECLARAȚIE CARE SE TERMINĂ CU O DECLARAȚIE în toate celelalte cazuri.

    • În special, în cazul în care apare o eroare într-un cmdlet/funcție avansată care acceptă doar un obiect de intrare SINGUR sau NU și care emite NU sau un obiect de ieșire SINGUR sau care ia doar un parametru de intrare și valorile parametrilor date împiedică o operațiune semnificativă.
      • În funcțiile avansate, trebuie să utilizați $PSCmdlet.ThrowTerminatingError() pentru a genera o eroare de încheiere a declarației.
      • Rețineți că, prin contrast, funcția Throw generează o eroare script-care întrerupe scriptul. întregul script (din punct de vedere tehnic: scriptul curent curent).
      • Gestionarea unei erori de încheiere a unei declarații: A try/catch handler sau trap poate fi utilizată o instrucțiune (care nu poate fi utilizată poate fi utilizat cu care nu se termină ), dar rețineți că chiar și declarație-care se termină în mod implicit nu împiedică restul scriptului să se execute. Ca și în cazul care nu se termină erori de terminare, $? reflectă $False dacă instrucțiunea anterioară a declanșat o eroare de terminare a instrucțiunii.

Din păcate, nu toate cmdlet-urile de bază ale PowerShell respectă aceste reguli:

  • Deși este puțin probabil să eșueze, New-TemporaryFile (PSv5+) ar raporta o eroare de nefinalizare dacă ar eșua, deși nu acceptă intrări de tip pipeline și produce doar un singur obiect de ieșire – acest lucru a fost corectat cel puțin începând cu PowerShell [Core] 7.0, totuși: a se vedea această problemă GitHub.

  • Resume-Job Ajutorul lui ‘s susține că trecerea unui tip de sarcină neacceptat (cum ar fi o sarcină creată cu Start-Job, care nu este acceptat, deoarece Resume-Job se aplică numai la fluxul de lucru jobs) provoacă o eroare de terminare, dar acest lucru nu este adevărat începând cu PSv5.1.

Michael Freidgeim

Adăugare la răspunsul lui Andy Arismendi:

Indiferent dacă Write-Error termină sau nu procesul depinde de $ErrorActionPreference setare.

Pentru scripturile non-triviale, $ErrorActionPreference = "Stop" este o setare recomandată pentru a eșua rapid.

„Comportamentul implicit al PowerShell în ceea ce privește erorile, care este de a continua în caz de eroare …pare foarte VB6 „On Error Resume Next””.”

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

Cu toate acestea, aceasta face ca Write-Error terminarea apelurilor.

Pentru a utiliza Write-Error ca o comandă care nu se termină, indiferent de alte setări de mediu, puteți utiliza parametru comun -ErrorAction cu valoarea Continue:

 Write-Error "Error Message" -ErrorAction:Continue

Rynant

Write-Error permite consumatorului funcției să suprime mesajul de eroare cu -ErrorAction SilentlyContinue (alternativ -ea 0). În timp ce throw necesită un try{...} catch {..}

Pentru a utiliza un try…catch cu Write-Error:

try {
    SomeFunction -ErrorAction Stop
}
catch {
    DoSomething
}

Comentarii

  • De ce trebuie să apelați Write-Error, dacă doriți să suprimați mesajul de eroare ? –  > Por Michael Freidgeim.
yzorg

Dacă citirea codului este corectă, atunci aveți dreptate. Terminarea erorilor ar trebui să utilizeze throw, iar dacă aveți de-a face cu tipuri .NET, atunci este util să urmați și convențiile de excepție .NET.