Tại sao 'tiếp tục' hoạt động giống như 'phá vỡ' trong Foreach-Object?


123

Nếu tôi làm như sau trong tập lệnh PowerShell:

$range = 1..100
ForEach ($_ in $range) {
    if ($_ % 7 -ne 0 ) { continue; }
    Write-Host "$($_) is a multiple of 7"
}

Tôi nhận được kết quả mong đợi là:

7 is a multiple of 7
14 is a multiple of 7
21 is a multiple of 7
28 is a multiple of 7
35 is a multiple of 7
42 is a multiple of 7
49 is a multiple of 7
56 is a multiple of 7
63 is a multiple of 7
70 is a multiple of 7
77 is a multiple of 7
84 is a multiple of 7
91 is a multiple of 7
98 is a multiple of 7

Tuy nhiên, nếu tôi sử dụng một đường ống và ForEach-Object, continuedường như thoát ra khỏi vòng lặp đường ống.

1..100 | ForEach-Object {
    if ($_ % 7 -ne 0 ) { continue; }
    Write-Host "$($_) is a multiple of 7"
}

Tôi có thể nhận được một continuehành vi giống như trong khi vẫn thực hiện ForEach-Object, vì vậy tôi không phải chia nhỏ đường dẫn của mình không?


Đây là một trang với rất nhiều lệnh để sử dụng với foreach: techotopia.com/index.php/...
bgmCoder

Tìm thấy một lời giải thích đàng hoàng và mẫu ở đây ... powershell.com/cs/blogs/tips/archive/2015/04/27/...
Nathan Hartley

Câu trả lời:


164

Đơn giản chỉ cần sử dụng returnthay vì continue. Điều này returntrả về từ khối script được gọi bởi ForEach-Objectmột lần lặp cụ thể, do đó, nó mô phỏng continuetrong một vòng lặp.

1..100 | ForEach-Object {
    if ($_ % 7 -ne 0 ) { return }
    Write-Host "$($_) is a multiple of 7"
}

Có một điểm cần lưu ý khi tái cấu trúc. Đôi khi người ta muốn chuyển đổi một foreachkhối câu lệnh thành một đường ống dẫn bằng một ForEach-Objectlệnh ghép ngắn (thậm chí nó còn có bí danh foreachgiúp thực hiện việc chuyển đổi này dễ dàng và cũng dễ mắc lỗi). Tất cả continues nên được thay thế bằng return.

Tái bút: Thật không may, nó không phải là dễ dàng để mô phỏng breaktrong ForEach-Object.


2
Từ những gì OP đang nói rõ ràng continuecó thể được sử dụng để mô phỏng một breaktrong ForEach-Object:)
Richard Hauer

6
@ Richard Hauer Như vậy continuesẽ phá vỡ toàn bộ tập lệnh, không chỉ ForEach-Objectnơi nó được sử dụng.
Roman Kuzmin

22

Bởi vì For-Eachđối tượng là một lệnh và không phải là một vòng lặp và continuebreakkhông áp dụng cho nó.

Ví dụ: nếu bạn có:

$b = 1,2,3

foreach($a in $b) {

    $a | foreach { if ($_ -eq 2) {continue;} else {Write-Host $_} }

    Write-Host  "after"
}

Bạn sẽ nhận được đầu ra là:

1
after
3
after

Đó là vì lệnh continueđược áp dụng cho vòng lặp foreach bên ngoài chứ không phải lệnh ghép ngắn foreach-object. Khi không có vòng lặp, cấp độ ngoài cùng, do đó tạo cho bạn ấn tượng về nó hoạt động như thế nào break.

Vậy làm thế nào để bạn có được một continuehành vi giống như vậy? Tất nhiên có một cách là Where-Object :

1..100 | ?{ $_ % 7  -eq 0} | %{Write-Host $_ is a multiple of 7}

Sử dụng lệnh ghép ngắn Where-Object là một gợi ý hay. Trong trường hợp thực tế của tôi, tôi không nghĩ rằng việc biến nhiều dòng mã đứng trước câu lệnh if thành một dòng mã dài khó đọc là hợp lý. Tuy nhiên, điều đó sẽ hiệu quả với tôi trong các tình huống khác.
Justin Diding

@JustinDiding - In my actual case, I don't think it makes sense to make the multiple lines of code preceding my if statement into a single long line of hard to read code.Ý bạn là gì?
manojlds

3
@manojlds có thể anh ấy nghĩ rằng giải pháp một dòng của bạn là "khó đọc", ít nhất đối với tôi là hoàn toàn ngược lại. Cách thức thực hiện mọi việc thực sự mạnh mẽ và rõ ràng và là cách tiếp cận chính xác cho những việc đơn giản như vậy. Viết mã trong shell mà không tận dụng được điều đó là vô nghĩa.
mjsr

Trong trường hợp của tôi, đây là câu trả lời đúng, hãy thêm điều kiện ở đâu để lọc ra các đối tượng mà tôi sẽ thực hiện tiếp tục hoặc quay lại để tôi không cần phải xử lý chúng ngay từ đầu. +1
Chris Magnuson

3

Một giải pháp thay thế khác là kiểu hack, nhưng bạn có thể bọc khối của mình trong một vòng lặp sẽ thực thi một lần. Bằng cách đó, continuesẽ có hiệu quả mong muốn:

1..100 | ForEach-Object {
    for ($cont=$true; $cont; $cont=$false) {
        if ($_ % 7 -ne 0 ) { continue; }
        Write-Host "$($_) is a multiple of 7"
    }
}

4
Thành thật mà nói, điều đó thật tệ :) Và không chỉ là một vụ hack vì thay vì đối tượng foreach, bạn cũng có thể sử dụng một vòng lặp foreach.
manojlds 13/10/11

1
@manojlds: 1..100 chỉ mang tính chất minh họa. do {} while ($ False) hoạt động tốt như vòng lặp for và trực quan hơn một chút.
Harry Martyrossian

2

Một elsecâu lệnh đơn giản làm cho nó hoạt động như sau:

1..100 | ForEach-Object {
    if ($_ % 7 -ne 0 ) {
        # Do nothing
    } else {
        Write-Host "$($_) is a multiple of 7"
    }
}

Hoặc trong một đường ống duy nhất:

1..100 | ForEach-Object { if ($_ % 7 -ne 0 ) {} else {Write-Host "$($_) is a multiple of 7"}}

Nhưng một giải pháp thanh lịch hơn là đảo ngược thử nghiệm của bạn và tạo ra đầu ra chỉ cho những thành công của bạn

1..100 | ForEach-Object {if ($_ % 7 -eq 0 ) {Write-Host "$($_) is a multiple of 7"}}
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.