เหตุใด 'ดำเนินการต่อ' จึงมีพฤติกรรมเหมือน 'ทำลาย' ใน Foreach-Object?


123

ถ้าฉันทำสิ่งต่อไปนี้ในสคริปต์ PowerShell:

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

ฉันได้ผลลัพธ์ที่คาดหวังของ:

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

แต่ถ้าผมใช้ท่อและForEach-Object, continueดูเหมือนว่าจะแยกออกจากวงท่อ

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

ฉันสามารถรับcontinueพฤติกรรมที่เหมือนในขณะที่ยังทำ ForEach-Object ได้หรือไม่ดังนั้นฉันจึงไม่ต้องแยกไปป์ไลน์ของฉัน


นี่คือหน้าที่มีคำสั่งมากมายให้ใช้กับforeach: techotopia.com/index.php/…
bgmCoder

พบคำอธิบายและตัวอย่างที่เหมาะสมที่นี่… powershell.com/
Nathan Hartley

คำตอบ:


164

เพียงใช้returnแทนไฟล์continue. สิ่งนี้returnส่งคืนจากบล็อกสคริปต์ซึ่งเรียกใช้โดยForEach-Objectการวนซ้ำเฉพาะดังนั้นจึงจำลองการcontinueวนซ้ำ

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

มี gotcha ที่ต้องคำนึงถึงเมื่อทำการ refactoring บางครั้งเราต้องการแปลงforeachบล็อกคำสั่งเป็นไปป์ไลน์ด้วยForEach-Objectcmdlet (มันมีนามแฝงforeachที่ช่วยให้การแปลงนี้ง่ายขึ้นและทำให้เกิดข้อผิดพลาดได้ง่ายเช่นกัน) ทั้งหมดcontinueควรถูกแทนที่ด้วยreturn.

PS: แต่น่าเสียดายที่มันไม่ได้เป็นเรื่องง่ายที่จะจำลองในbreakForEach-Object


2
จากสิ่งที่ OP พูดเห็นได้ชัดว่าcontinueสามารถใช้เพื่อจำลองbreakในForEach-Object:)
Richard Hauer

6
@ Richard Hauer สิ่งcontinueนี้จะทำลายสคริปต์ทั้งหมดไม่ใช่แค่ForEach-Objectที่ที่ใช้
Roman Kuzmin

22

เพราะFor-Eachวัตถุที่เป็น cmdlet และไม่ได้เป็นห่วงและcontinueและbreakไม่สามารถใช้กับมัน

ตัวอย่างเช่นหากคุณมี:

$b = 1,2,3

foreach($a in $b) {

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

    Write-Host  "after"
}

คุณจะได้ผลลัพธ์เป็น:

1
after
3
after

เป็นเพราะcontinueรับไปใช้กับ foreach loop ด้านนอกไม่ใช่ cmdlet foreach-object breakในกรณีที่ไม่มีห่วงในระดับสุดจึงให้คุณเป็นที่ประทับใจของมันทำตัวเหมือน

แล้วคุณจะมีcontinueพฤติกรรมที่เหมือนได้อย่างไร? ทางเดียวคือWhere-Objectแน่นอน:

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

การใช้ cmdlet Where-Object เป็นคำแนะนำที่ดี ในกรณีจริงของฉันฉันไม่คิดว่ามันสมเหตุสมผลที่จะทำให้โค้ดหลายบรรทัดที่อยู่ข้างหน้าคำสั่ง if ของฉันเป็นโค้ดที่อ่านยากเพียงบรรทัดเดียว อย่างไรก็ตามนั่นจะได้ผลสำหรับฉันในสถานการณ์อื่น ๆ
Justin Dearing

@JustinDearing - 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.หมายความว่าไง?
manojlds

3
@manojlds บางทีเขาอาจคิดว่าวิธีแก้ปัญหาแบบบรรทัดเดียวของคุณ "อ่านยาก" อย่างน้อยสำหรับฉันก็เป็นไปในทางตรงกันข้าม วิธีการไปป์ไลน์ในการทำสิ่งต่างๆนั้นมีประสิทธิภาพและชัดเจนและเป็นแนวทางที่ถูกต้องสำหรับสิ่งง่ายๆเช่นนั้น การเขียนโค้ดในเชลล์โดยไม่ใช้ประโยชน์จากสิ่งนั้นไม่มีจุดหมาย
mjsr

ในกรณีของฉันนี่เป็นคำตอบที่ถูกต้องให้เพิ่มเงื่อนไขที่เพื่อกรองออบเจ็กต์ที่ฉันจะดำเนินการต่อหรือส่งคืนเพื่อที่ฉันจะได้ไม่ต้องประมวลผลตั้งแต่แรก +1
Chris Magnuson

3

อีกทางเลือกหนึ่งคือการแฮ็ก แต่คุณสามารถห่อบล็อกของคุณในวงที่จะดำเนินการครั้งเดียว ด้วยวิธีนี้continueจะมีผลตามที่ต้องการ:

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

4
ตรงไปตรงมานั่นน่าเกลียด :) และไม่ใช่แค่แฮ็คเพราะแทนที่จะใช้ foreach-object คุณสามารถใช้ foreach loop ได้เช่นกัน
manojlds

1
@manojlds: 1..100 เป็นเพียงภาพประกอบเท่านั้น do {} while ($ False) ใช้งานได้เช่นเดียวกับการวนซ้ำและใช้งานง่ายขึ้นเล็กน้อย
Harry Martyrossian

2

elseคำสั่งง่ายๆทำให้มันทำงานได้ใน:

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

หรือในท่อเดียว:

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

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

1..100 | ForEach-Object {if ($_ % 7 -eq 0 ) {Write-Host "$($_) is a multiple of 7"}}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.