Khi nào tôi nên sử dụng Viết-Lỗi so với Ném? Lỗi kết thúc so với không kết thúc


145

Nhìn vào tập lệnh Get-WebFile trên PoshCode, http://posehcode.org/3226 , tôi nhận thấy điều này rất lạ với tôi:

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

Lý do cho điều này trái ngược với điều sau đây là gì?

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

Hoặc thậm chí tốt hơn:

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

Theo tôi hiểu, bạn nên sử dụng Lỗi ghi cho các lỗi không kết thúc và Ném để chấm dứt các lỗi, do đó, có vẻ như tôi không nên sử dụng Lỗi ghi theo sau là Trả về. Có sự khác biệt?


4
Ý anh là gì? Nếu Write_error cho phép tập lệnh tiếp tục, việc có câu lệnh trả về sau Lỗi ghi là điều rất dễ hiểu. Lỗi đã được ghi ra và bạn quay trở lại mã được gọi là hàm ở vị trí đầu tiên. Vì Ném là để chấm dứt lỗi, nó sẽ tự động chấm dứt để tuyên bố trả lại trong tuyên bố ném là vô ích
Gisli

1
@Gisli: Điều quan trọng cần lưu ý đó là returnkhông không trở lại cho người gọi trong processkhối chức năng (nâng cao); thay vào đó, nó tiến tới đối tượng đầu vào tiếp theo trong đường ống. Thật vậy, đây là kịch bản điển hình để tạo ra các lỗi không kết thúc: nếu vẫn có thể xử lý các đối tượng đầu vào tiếp theo.
mkuity0

1
Lưu ý rằng Throwtạo ra lỗi sắp hết tập lệnh , không giống với lỗi sắp hết câu lệnh được kích hoạt, ví dụ, bởi Get-Item -NoSuchParameterhoặc 1 / 0.
mkuity0

Câu trả lời:


188

Write-Errornên được sử dụng nếu bạn muốn thông báo cho người dùng về một lỗi không nghiêm trọng. Theo mặc định, tất cả những gì nó làm là in một thông báo lỗi bằng văn bản màu đỏ trên bàn điều khiển. Nó không ngăn chặn một đường ống hoặc một vòng lặp tiếp tục. Throwmặt khác tạo ra cái được gọi là lỗi kết thúc. Nếu bạn sử dụng throw, đường ống và / hoặc vòng lặp hiện tại sẽ bị chấm dứt. Trong thực tế, tất cả các thực thi sẽ bị chấm dứt trừ khi bạn sử dụng một traphoặc một try/catchcấu trúc để xử lý lỗi kết thúc.

Có một điều cần lưu ý, nếu bạn thiết lập $ErrorActionPreferenceđể"Stop" và sử dụng Write-Errornó sẽ tạo ra một lỗi chấm dứt .

Trong tập lệnh bạn liên kết với chúng tôi tìm thấy điều này:

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
   }

Có vẻ như tác giả của chức năng đó muốn dừng việc thực thi chức năng đó và hiển thị thông báo lỗi trên màn hình nhưng không muốn toàn bộ tập lệnh dừng thực thi. Tác giả kịch bản có thể đã sử dụng throwtuy nhiên điều đó có nghĩa là bạn sẽ phải sử dụng try/catchkhi gọi hàm.

returnsẽ thoát khỏi phạm vi hiện tại có thể là một hàm, tập lệnh hoặc khối tập lệnh. Điều này được minh họa tốt nhất với mã:

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

Đầu ra cho cả hai:

1
2
3
4
5

Một gotcha ở đây đang sử dụng returnvới ForEach-Object. Nó sẽ không phá vỡ xử lý như người ta có thể mong đợi.

Thêm thông tin:


Ok, vì vậy Ném sẽ dừng mọi thứ, Lỗi ghi + trả về sẽ chỉ dừng chức năng hiện tại.
Bill Barry

@BillBarry Tôi đã cập nhật câu trả lời của mình một chút với lời giải thích return.
Andy Arismendi

Điều gì về Lỗi ghi theo sau là thoát (1) để đảm bảo mã lỗi thích hợp được trả về HĐH? Điều đó có bao giờ thích hợp không?
pabram

19

Sự khác biệt chính giữa lệnh ghép ngắn Lỗi ghi và từ khóa throw trong PowerShell là cái trước chỉ đơn giản là in một số văn bản sang luồng lỗi tiêu chuẩn (stderr) , trong khi cái sau thực sự chấm dứt việc xử lý lệnh hoặc hàm đang chạy, sau đó được xử lý bởi PowerShell bằng cách gửi thông tin về lỗi đến bàn điều khiển.

Bạn có thể quan sát hành vi khác nhau của hai người trong các ví dụ bạn cung cấp:

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

Trong ví dụ này, returntừ khóa đã được thêm vào để dừng việc thực thi tập lệnh một cách rõ ràng sau khi thông báo lỗi được gửi đến bàn điều khiển. Trong ví dụ thứ hai, mặt khác, returntừ khóa là không cần thiết vì việc chấm dứt được thực hiện hoàn toàn bởi throw:

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

2
nếu bạn có $ ErrorActionPreference = "Dừng", Lỗi ghi cũng sẽ chấm dứt quá trình.
Michael Freidgeim

4
Thông tin tốt, nhưng khi luồng lỗi PowerShell là tương tự với văn bản dựa trên dòng stderr trong vỏ khác, giống như tất cả PowerShell suối nó chứa đối tượng , cụ thể là [System.Management.Automation.ErrorRecord]trường hợp, đó là theo mặc định thu thập trong tự động $Errorthu ( $Error[0]chứa các lỗi gần đây nhất). Ngay cả khi bạn chỉ sử dụng Write-Errorvới một chuỗi , chuỗi đó sẽ được bọc trong một [System.Management.Automation.ErrorRecord]thể hiện.
mkuity0

16

Quan trọng : Có 2 loại lỗi chấm dứt , mà chủ đề trợ giúp hiện không may conflate :

  • tuyên bố -terminating lỗi, theo báo cáo của cmdlet trong một số tình huống không thể phục hồi được và bởi các biểu thức trong đó một ngoại lệ .NET / A lỗi PS runtime xảy ra; chỉ có câu lệnh kết thúc và việc thực thi tập lệnh tiếp tục theo mặc định .

  • kịch bản -terminating lỗi (chính xác hơn: runspace-chấm dứt ), là một trong hai kích hoạt bởiThrowhoặc bằng cách leo thang một trong những loại lỗi khác qua lỗi-action giá trị ưu tiên biến / tham sốStop.
    Trừ khi bị bắt, chúng chấm dứt không gian hiện tại (luồng); nghĩa là, họ chấm dứt không chỉ tập lệnh hiện tại, mà tất cả những người gọi nó cũng vậy, nếu có).

Để biết tổng quan toàn diện về xử lý lỗi của PowerShell, hãy xem vấn đề về tài liệu GitHub này .

Phần còn lại của bài đăng này tập trung vào các lỗi không kết thúc so với kết thúc câu lệnh .


Để bổ sung cho câu trả lời hữu ích hiện có với một tập trung vào cốt lõi của câu hỏi: Làm thế nào để bạn chọn xem có nên báo cáo Câu lệnh chấm dứt hoặc không chấm dứt lỗi ?

Báo cáo lỗi Cmdlet chứa các hướng dẫn hữu ích; hãy để tôi thử một bản tóm tắt thực dụng :

Ý tưởng chung đằng sau phi chấm dứt lỗi là cho phép "fault-tolerant" chế biến các bộ đầu vào lớn : thất bại trong việc xử lý một tập hợp con của các đối tượng đầu vào nên không (theo mặc định) hủy bỏ sự - có khả năng kéo dài - Quá trình như một toàn thể , cho phép bạn kiểm tra lỗi và chỉ xử lý lại các đối tượng bị lỗi sau - như được báo cáo thông qua các bản ghi lỗi được thu thập trong biến tự động $Error.

  • Báo cáo lỗi KHÔNG KẾT THÚC , nếu lệnh ghép ngắn / chức năng nâng cao của bạn:

    • chấp nhận các đối tượng đầu vào MULTIPLE , thông qua đầu vào đường ống và / hoặc các tham số có giá trị mảng, VÀ
    • xảy ra lỗi đối với các đối tượng đầu vào CỤ THỂ , VÀ
    • những lỗi này KHÔNG CHẾ BIẾN TRƯỚC các đối tượng đầu vào THÊM TRONG NGUYÊN TẮC ( tình huống , có thể không còn đối tượng đầu vào nào còn lại và / hoặc các đối tượng đầu vào trước đó có thể đã được xử lý thành công).
      • Trong các chức năng nâng cao, sử dụng $PSCmdlet.WriteError()để báo cáo lỗi không kết thúc ( Write-Errorthật không may, không gây ra $?được đặt thành $Falsetrong phạm vi của người gọi - xem vấn đề GitHub này ).
      • Xử lý lỗi không kết thúc: $?cho bạn biết liệu lệnh gần đây nhất đã báo cáo ít nhất một lỗi không kết thúc.
        • Do đó, $?con người $Falsecó thể một trong hai trung bình mà bất kỳ (không rỗng) tập hợp con của các đối tượng đầu vào không được xử lý đúng cách, có thể là toàn bộ thiết lập.
        • Biến ưu tiên $ErrorActionPreferencevà / hoặc tham số cmdlet phổ biến -ErrorActioncó thể sửa đổi hành vi của các lỗi không kết thúc (chỉ) về mặt hành vi đầu ra lỗi và liệu các lỗi không kết thúc có nên được chuyển thành các lỗi kết thúc tập lệnh hay không.
  • Báo cáo lỗi TUYÊN BỐ trong tất cả các trường hợp khác .

    • Đáng chú ý, nếu xảy ra lỗi trong một lệnh ghép ngắn / hàm nâng cao chỉ chấp nhận một đối tượng đầu vào SINGLE hoặc NO và đầu ra NO hoặc một đối tượng đầu ra SINGLE hoặc chỉ lấy đầu vào tham số và các giá trị tham số được cung cấp sẽ ngăn hoạt động có ý nghĩa.
      • Trong các chức năng nâng cao, bạn phải sử dụng $PSCmdlet.ThrowTerminatingError()để tạo ra lỗi kết thúc câu lệnh.
      • Lưu ý rằng, ngược lại, Throwtừ khóa tạo ra lỗi sắp xếp theo kịch bản hủy bỏ toàn bộ kịch bản (về mặt kỹ thuật: dòng chủ đề ).
      • Xử lý lỗi kết thúc câu lệnh: Có thể sử dụng try/catchtrình xử lý hoặc trapcâu lệnh ( không thể sử dụng với các lỗi không kết thúc ), nhưng lưu ý rằng ngay cả các lỗi kết thúc câu lệnh theo mặc định cũng không ngăn phần còn lại của tập lệnh chạy. Như với các lỗi không kết thúc , $?phản ánh $Falsenếu câu lệnh trước đó gây ra lỗi kết thúc câu lệnh.

Đáng buồn thay, không phải tất cả các lệnh ghép ngắn lõi của PowerShell đều chơi theo các quy tắc sau:

  • Mặc dù không thể thất bại, New-TemporaryFile(PSv5 +) sẽ báo cáo lỗi không kết thúc nếu không thành công, mặc dù không chấp nhận đầu vào đường ống và chỉ tạo một đối tượng đầu ra - tuy nhiên, điều này đã được sửa chữa ít nhất là PowerShell [Core] 7.0, tuy nhiên: hãy xem GitHub này vấn đề .

  • Resume-JobTrợ giúp tuyên bố rằng việc chuyển loại công việc không được hỗ trợ (chẳng hạn như công việc được tạo bằng Start-Job, không được hỗ trợ, vì Resume-Jobchỉ áp dụng cho công việc trong quy trình công việc) gây ra lỗi kết thúc, nhưng điều đó không đúng với PSv5.1.


8

Write-Errorcho phép người tiêu dùng của chức năng chặn thông báo lỗi bằng -ErrorAction SilentlyContinue(cách khác -ea 0). Trong khi throwyêu cầu mộttry{...} catch {..}

Để sử dụng thử ... bắt với Write-Error:

try {
    SomeFunction -ErrorAction Stop
}
catch {
    DoSomething
}

Tại sao bạn cần phải gọi Lỗi ghi, nếu bạn muốn chặn thông báo lỗi?
Michael Freidgeim

8

Ngoài câu trả lời của Andy Arismendi :

Cho dù Write-Lỗi chấm dứt quá trình hay không phụ thuộc vào các $ErrorActionPreferencethiết lập.

Đối với các tập lệnh không tầm thường, $ErrorActionPreference = "Stop"là một cài đặt được đề xuất để thất bại nhanh.

"Hành vi mặc định của PowerShell liên quan đến lỗi, đó là tiếp tục xảy ra lỗi ... cảm thấy rất VB6 Lỗi khi tiếp tục lại Tiếp theo -ish"

(từ http://codebetter.com/jameskovacs/2010/02/25/the-exec-pro Hiệu / )

Tuy nhiên, nó làm cho Write-Errorcác cuộc gọi chấm dứt.

Để sử dụng Lỗi ghi như một lệnh không kết thúc bất kể các cài đặt môi trường khác, bạn có thể sử dụng tham số chung -ErrorAction có giá trị Continue:

 Write-Error "Error Message" -ErrorAction:Continue

0

Nếu việc đọc mã của bạn là chính xác thì bạn đã đúng. Lỗi chấm dứt nên sử dụng throwvà nếu bạn đang xử lý các loại .NET thì cũng hữu ích khi tuân theo các quy ước ngoại lệ .NET.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.