เมื่อใดที่ฉันควรใช้ข้อผิดพลาดในการเขียนเทียบกับการโยน การยุติและข้อผิดพลาดที่ไม่สิ้นสุด


145

ดูที่สคริปต์ Get-WebFile บน PoshCode, http://poshcode.org/3226ฉันสังเกตเห็นการคุมกำเนิดที่แปลกประหลาดนี้:

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

อะไรคือสาเหตุของสิ่งนี้เมื่อเทียบกับสิ่งต่อไปนี้?

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

หรือดีกว่า:

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

ดังที่ฉันเข้าใจแล้วคุณควรใช้ Write-Error สำหรับข้อผิดพลาดที่ไม่สิ้นสุดและการโยนเพื่อยกเลิกข้อผิดพลาดดังนั้นฉันคิดว่าคุณไม่ควรใช้ Write-Error ตามด้วย Return มีความแตกต่างหรือไม่?


4
คุณหมายถึงอะไร หาก Write_error อนุญาตให้สคริปต์ดำเนินการต่อเป็นที่เข้าใจได้อย่างดีว่าจะมีคำสั่งส่งคืนหลังจากเขียนข้อผิดพลาด ข้อผิดพลาดได้ถูกเขียนออกมาแล้วและคุณกลับไปที่รหัสที่เรียกว่าฟังก์ชันในตอนแรก ตั้งแต่โยนคือความผิดพลาดยุติมันจะสิ้นสุดลงโดยอัตโนมัติเพื่อให้คำสั่งกลับในการประกาศจากเส้นข้างจะไม่ได้ผล
Gisli

1
@Gisli: มันเป็นสิ่งสำคัญที่จะทราบว่าreturnไม่ได้กลับไปที่โทรในที่processบล็อกของ (ขั้นสูง) ฟังก์ชั่น; แต่จะดำเนินต่อไปยังวัตถุอินพุตถัดไปในไปป์ไลน์ อันที่จริงนี่เป็นสถานการณ์ทั่วไปสำหรับการสร้างข้อผิดพลาดที่ไม่สิ้นสุด: หากการประมวลผลวัตถุอินพุตเพิ่มเติมยังคงเป็นไปได้
mklement0

1
โปรดทราบว่าการThrowสร้างสคริปต์ผิดพลาด -terminating ซึ่งไม่ได้เป็นเช่นเดียวกับคำสั่ง -terminating ข้อผิดพลาดเรียกเช่นโดยการหรือGet-Item -NoSuchParameter 1 / 0
mklement0

คำตอบ:


189

Write-Errorควรใช้หากคุณต้องการแจ้งให้ผู้ใช้ทราบถึงข้อผิดพลาดที่ไม่ร้ายแรง โดยค่าเริ่มต้นสิ่งที่ทำได้คือพิมพ์ข้อความแสดงข้อผิดพลาดเป็นข้อความสีแดงบนคอนโซล มันไม่ได้หยุดไปป์ไลน์หรือวนจากการดำเนินการต่อ Throwในทางตรงกันข้ามผลิตสิ่งที่เรียกว่าข้อผิดพลาดการยุติ หากคุณใช้การโยนไปป์ไลน์และ / หรือลูปปัจจุบันจะถูกยกเลิก ในความเป็นจริงการดำเนินการทั้งหมดจะถูกยกเลิกเว้นแต่คุณจะใช้trapหรือtry/catchโครงสร้างเพื่อจัดการข้อผิดพลาดการยกเลิก

มีสิ่งหนึ่งที่จะต้องทราบคือถ้าคุณตั้งค่า$ErrorActionPreferenceการ"Stop"และใช้Write-Errorมันจะสร้างข้อผิดพลาดยุติ

ในสคริปต์ที่คุณเชื่อมโยงกับเราพบสิ่งนี้:

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
   }

ดูเหมือนว่าผู้เขียนฟังก์ชั่นนั้นต้องการหยุดการทำงานของฟังก์ชั่นนั้นและแสดงข้อความข้อผิดพลาดบนหน้าจอ แต่ไม่ต้องการให้สคริปต์ทั้งหมดหยุดการทำงาน ผู้เขียนสคริปต์สามารถใช้งานได้throwแต่ก็หมายความว่าคุณจะต้องใช้ a try/catchเมื่อเรียกใช้ฟังก์ชัน

returnจะออกจากขอบเขตปัจจุบันซึ่งอาจเป็นฟังก์ชันสคริปต์หรือบล็อกสคริปต์ นี่คือตัวอย่างที่ดีที่สุดด้วยรหัส:

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

เอาต์พุตสำหรับทั้งสอง:

1
2
3
4
5

หนึ่ง gotcha ที่นี่จะใช้กับreturn ForEach-Objectมันจะไม่หยุดการประมวลผลอย่างที่คาดไว้

ข้อมูลมากกว่านี้:


ตกลงดังนั้น Throw จะหยุดทุกอย่าง Write-Error + return จะหยุดเฉพาะฟังก์ชั่นปัจจุบันเท่านั้น
Bill Barry

@BillBarry returnฉันปรับปรุงคำตอบของฉันเป็นบิตที่มีคำอธิบายของ
Andy Arismendi

สิ่งที่เกี่ยวกับการเขียนข้อผิดพลาดตามด้วยออก (1) เพื่อให้แน่ใจว่ารหัสข้อผิดพลาดที่เหมาะสมกลับสู่ระบบปฏิบัติการ? มันเหมาะสมหรือไม่
pabrams

19

ความแตกต่างที่สำคัญระหว่างการเขียนข้อผิดพลาด cmdlet และโยนคำหลักใน PowerShell เป็นว่าอดีตเพียงแค่พิมพ์ข้อความบางส่วนกับกระแสข้อผิดพลาดมาตรฐาน (stderr)ในขณะที่หลังจริงยุติการประมวลผลของคำสั่งที่ทำงานหรือฟังก์ชั่นซึ่งจะแล้วจัดการ โดย PowerShell โดยส่งข้อมูลเกี่ยวกับข้อผิดพลาดไปยังคอนโซล

คุณสามารถสังเกตพฤติกรรมที่แตกต่างของทั้งสองในตัวอย่างที่คุณให้ไว้:

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

ในตัวอย่างนี้returnมีการเพิ่มคำหลักเพื่อหยุดการทำงานของสคริปต์อย่างชัดเจนหลังจากข้อความแสดงข้อผิดพลาดถูกส่งออกไปยังคอนโซล ในตัวอย่างที่สองในทางกลับกันreturnคำหลักนั้นไม่จำเป็นเนื่องจากการยกเลิกจะกระทำโดยปริยายโดยthrow:

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

2
หากคุณมี $ ErrorActionPreference = "หยุด" ข้อผิดพลาดในการเขียนจะยุติกระบวนการเช่นกัน
Michael Freidgeim

4
ข้อมูลที่ดี แต่ในขณะที่กระแสข้อผิดพลาด PowerShell เป็นคล้ายกับข้อความตามกระแส stderr ในเปลือกหอยอื่น ๆ เช่นทุก PowerShell ลำธารมันมีวัตถุคือ[System.Management.Automation.ErrorRecord]กรณีซึ่งเกิดขึ้นโดยเริ่มต้นในการเก็บรวบรวมอัตโนมัติ$Errorคอลเลกชัน ( $Error[0]มีข้อผิดพลาดล่าสุด) แม้ว่าคุณเพิ่งใช้Write-Errorกับสตริงสตริงนั้นจะถูกห่อใน[System.Management.Automation.ErrorRecord]อินสแตนซ์
mklement0

17

สำคัญ : มีข้อผิดพลาดในการยกเลิก2ประเภทซึ่งหัวข้อความช่วยเหลือปัจจุบันน่าเสียดายที่ทำให้สับสน :

  • คำสั่ง -terminatingข้อผิดพลาดขณะที่รายงานจาก cmdlets ในสถานการณ์ที่ไม่สามารถกู้คืนบางอย่างและโดยการแสดงออกซึ่งเป็นข้อยกเว้น .NET / ข้อผิดพลาด runtime PS เกิดขึ้น; เพียงคำสั่งถูกยกเลิกและเรียกสคริปต์ยังคงเป็นค่าเริ่มต้น

  • สคริปต์ -terminatingข้อผิดพลาด (ถูกต้องมากขึ้น: runspace-ยุติ ) รวมทั้งโดยเรียกThrowหรือโดยการเพิ่มขึ้นหนึ่งในประเภทที่ข้อผิดพลาดอื่น ๆ Stopผ่านทางข้อผิดพลาดการดำเนินการตั้งค่าตัวแปรค่าพารามิเตอร์
    ยกเว้นว่าถูกจับพวกมันจะยกเลิก runspace ปัจจุบัน (เธรด) นั่นคือพวกเขาจะยุติไม่เพียง แต่สคริปต์ปัจจุบัน แต่ผู้โทรทั้งหมดจะถูกยกเลิกด้วยถ้ามี)

สำหรับภาพรวมที่ครอบคลุมของการจัดการข้อผิดพลาด PowerShell ให้ดูปัญหานี้เอกสาร GitHub

ส่วนที่เหลือของโพสต์นี้มุ่งเน้นไปที่ข้อผิดพลาดที่ไม่สิ้นสุดและเทียบกับการยกเลิกคำสั่ง


เพื่อเติมเต็มคำตอบที่เป็นประโยชน์ที่มีอยู่มีความสำคัญกับหลักของคำถามนี้: คุณจะเลือกว่าจะรายงานคำสั่งที่ยกเลิกหรือไม่ใช่การยุติข้อผิดพลาด ?

การรายงานข้อผิดพลาด Cmdletมีแนวทางที่เป็นประโยชน์; ให้ฉันลองสรุปอย่างจริงจัง :

ความคิดทั่วไปที่อยู่เบื้องหลังที่ไม่ใช่การยุติข้อผิดพลาดคือการให้ "ความผิดพลาด" การประมวลผลของชุดการป้อนข้อมูลขนาดใหญ่ : ความล้มเหลวในการประมวลผลส่วนย่อยของวัตถุการป้อนข้อมูลที่ไม่ควร (ค่าเริ่มต้น) ยกเลิก - อาจยาวทำงาน - ขั้นตอนการรวม , ช่วยให้คุณสามารถตรวจสอบข้อผิดพลาดและประมวลผลใหม่เพียงล้มเหลววัตถุในภายหลัง - $Errorตามที่รายงานผ่านทางบันทึกข้อผิดพลาดที่เก็บในตัวแปรอัตโนมัติ

  • รายงานข้อผิดพลาดที่ไม่ใช่การยุติหากฟังก์ชัน cmdlet / ขั้นสูงของคุณ:

    • ยอมรับวัตถุอินพุตหลายทางผ่านอินพุตไปป์ไลน์และ / หรือพารามิเตอร์ที่มีค่าอาร์เรย์และ
    • เกิดข้อผิดพลาดสำหรับวัตถุอินพุตที่ระบุและ
    • ข้อผิดพลาดเหล่านี้ไม่ได้ป้องกันการประมวลผลของวัตถุอินพุตเพิ่มเติมในหลักการ ( สถานการณ์อาจไม่มีวัตถุอินพุตเหลืออยู่และ / หรือวัตถุอินพุตก่อนหน้าอาจถูกประมวลผลเรียบร้อยแล้ว)
      • ในฟังก์ชั่นขั้นสูงใช้$PSCmdlet.WriteError()เพื่อรายงานข้อผิดพลาดที่ไม่สิ้นสุด ( Write-Errorแต่น่าเสียดายที่ไม่ได้$?กำหนดให้$Falseอยู่ในขอบเขตของผู้โทร - ดูปัญหา GitHub นี้ )
      • การจัดการข้อผิดพลาดที่ไม่สิ้นสุด: $?บอกคุณว่าคำสั่งล่าสุดรายงานข้อผิดพลาดที่ไม่สิ้นสุดอย่างน้อยหนึ่งข้อ
        • ดังนั้น$?ความ$Falseหมายอาจเป็นได้ว่าชุดย่อยของวัตถุอินพุต(ไม่ว่าง) ใด ๆไม่ได้รับการประมวลผลอย่างเหมาะสมอาจเป็นได้ทั้งชุด
        • ตัวแปรการตั้งค่า$ErrorActionPreferenceและ / หรือพารามิเตอร์ cmdlet ทั่วไป-ErrorActionสามารถปรับเปลี่ยนพฤติกรรมของข้อผิดพลาดที่ไม่สิ้นสุด (เท่านั้น) ในแง่ของพฤติกรรมการส่งออกข้อผิดพลาดและไม่ว่าข้อผิดพลาดที่ไม่สิ้นสุดควรถูกส่งไปยังสคริปต์ที่ทำลาย
  • รายงานคำสั่งยุติการผิดพลาดในกรณีอื่น

    • โดยเฉพาะอย่างยิ่งหากมีข้อผิดพลาดเกิดขึ้นในฟังก์ชัน cmdlet / ขั้นสูงที่ยอมรับเฉพาะอินพุตวัตถุเดียวและไม่ใช่และเอาท์พุท NO หรือวัตถุเอาท์พุทเดียวหรือใช้พารามิเตอร์อินพุตเท่านั้นและค่าพารามิเตอร์ที่กำหนดป้องกันการดำเนินงานที่มีความหมาย
      • ในฟังก์ชั่นขั้นสูงคุณต้องใช้$PSCmdlet.ThrowTerminatingError()เพื่อสร้างข้อผิดพลาดการยกเลิกคำสั่ง
      • โปรดทราบว่าโดยทางตรงกันข้ามThrowคำหลักสร้างสคริปต์ข้อผิดพลาดที่ -terminating ยกเลิกสคริปต์ทั้งหมด (เทคนิค: ปัจจุบันด้าย )
      • การจัดการข้อผิดพลาดในการยกเลิกคำสั่ง: อาจใช้try/catchตัวจัดการหรือtrapข้อความสั่ง (ซึ่งไม่สามารถใช้กับข้อผิดพลาดที่ไม่สิ้นสุด ) แต่โปรดทราบว่าแม้คำสั่งการทำลายข้อผิดพลาดโดยค่าเริ่มต้นจะไม่ป้องกันส่วนที่เหลือของสคริปต์ เช่นเดียวกับข้อผิดพลาดที่ไม่สิ้นสุด$?สะท้อนให้เห็น$Falseว่าคำสั่งก่อนหน้านี้ก่อให้เกิดข้อผิดพลาดการยกเลิกคำสั่ง

น่าเศร้าที่ cmdlet หลักของ PowerShell ไม่ได้เล่นตามกฎเหล่านี้ทั้งหมด :

  • ในขณะที่ไม่น่าจะล้มเหลวNew-TemporaryFile(PSv5 +) จะรายงานข้อผิดพลาดที่ไม่สิ้นสุดหากล้มเหลวแม้จะไม่รับอินพุตไปป์ไลน์และสร้างเพียงหนึ่งออบเจกต์เอาต์พุต - สิ่งนี้ได้รับการแก้ไขอย่างน้อย PowerShell [Core] 7.0 อย่างไรก็ตาม: ดูGitHub นี้ ปัญหา

  • Resume-Jobความช่วยเหลือของอ้างว่าการส่งประเภทงานที่ไม่ได้รับการสนับสนุน (เช่นงานที่สร้างด้วยStart-Jobซึ่งไม่ได้รับการสนับสนุนเพราะResume-Jobใช้กับงานเวิร์กโฟลว์เท่านั้น) ทำให้เกิดข้อผิดพลาดในการยกเลิก แต่นั่นไม่เป็นความจริงเหมือน PSv5.1


8

Write-Errorอนุญาตให้ผู้ใช้ฟังก์ชั่นระงับข้อความแสดงข้อผิดพลาดด้วย-ErrorAction SilentlyContinue(หรือมิฉะนั้น-ea 0) ในขณะที่throwต้องมีtry{...} catch {..}

วิธีใช้ลอง ... จับด้วยWrite-Error:

try {
    SomeFunction -ErrorAction Stop
}
catch {
    DoSomething
}

ทำไมคุณต้องโทรหาข้อผิดพลาดการเขียนทั้งหมดถ้าคุณต้องการที่จะระงับข้อความข้อผิดพลาด?
Michael Freidgeim

8

นอกจากคำตอบของ Andy Arismendi :

การเขียนข้อผิดพลาด จะยุติกระบวนการหรือไม่ขึ้นอยู่กับการ$ErrorActionPreferenceตั้งค่า

สำหรับสคริปต์ที่ไม่สำคัญ$ErrorActionPreference = "Stop"คือการตั้งค่าที่แนะนำให้ล้มเหลวอย่างรวดเร็ว

"พฤติกรรมเริ่มต้นของ PowerShell เกี่ยวกับข้อผิดพลาดซึ่งจะดำเนินต่อไปในข้อผิดพลาด ... รู้สึก VB6 มาก" เมื่อเกิดข้อผิดพลาดต่อไป "-"

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

อย่างไรก็ตามมันทำให้การWrite-Errorโทรสิ้นสุดลง

ในการใช้Write-Errorเป็นคำสั่งที่ไม่สิ้นสุดโดยไม่คำนึงถึงการตั้งค่าสภาพแวดล้อมอื่น ๆ คุณสามารถใช้พารามิเตอร์ทั่วไปที่ -ErrorActionมีค่าContinue:

 Write-Error "Error Message" -ErrorAction:Continue

0

หากการอ่านรหัสถูกต้องแสดงว่าคุณถูกต้อง ข้อผิดพลาดในการยกเลิกควรใช้throwและหากคุณกำลังจัดการกับ. NET ประเภทนั้นมันจะเป็นประโยชน์ที่จะปฏิบัติตามอนุสัญญายกเว้น. NET

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.