How to exit from ForEach-Object in PowerShell

前端 未结 9 1734
北海茫月
北海茫月 2020-11-27 20:30

I have the following code:

$project.PropertyGroup | Foreach-Object {
    if($_.GetAttribute(\'Condition\').Trim() -eq $propertyGroupConditionName.Trim()) {
           


        
相关标签:
9条回答
  • 2020-11-27 21:05

    You have two options to abruptly exit out of ForEach-Object pipeline in PowerShell:

    1. Apply exit logic in Where-Object first, then pass objects to Foreach-Object, or
    2. (where possible) convert Foreach-Object into a standard Foreach looping construct.

    Let's see examples: Following scripts exit out of Foreach-Object loop after 2nd iteration (i.e. pipeline iterates only 2 times)":

    Solution-1: use Where-Object filter BEFORE Foreach-Object:

    [boolean]$exit = $false;
    1..10 | Where-Object {$exit -eq $false} | Foreach-Object {
         if($_ -eq 2) {$exit = $true}    #OR $exit = ($_ -eq 2);
         $_;
    }
    

    OR

    1..10 | Where-Object {$_ -le 2} | Foreach-Object {
         $_;
    }
    

    Solution-2: Converted Foreach-Object into standard Foreach looping construct:

    Foreach ($i in 1..10) { 
         if ($i -eq 3) {break;}
         $i;
    }
    

    PowerShell should really provide a bit more straightforward way to exit or break out from within the body of a Foreach-Object pipeline. Note: return doesn't exit, it only skips specific iteration (similar to continue in most programming languages), here is an example of return:

    Write-Host "Following will only skip one iteration (actually iterates all 10 times)";
    1..10 | Foreach-Object {
         if ($_ -eq 3) {return;}  #skips only 3rd iteration.
         $_;
    }
    

    HTH

    0 讨论(0)
  • 2020-11-27 21:06

    There are differences between foreach and foreach-object.

    A very good description you can find here: MS-ScriptingGuy

    For testing in PS, here you have scripts to show the difference.

    ForEach-Object:

    # Omit 5.
    1..10 | ForEach-Object {
    if ($_ -eq 5) {return}
    # if ($_ -ge 5) {return} # Omit from 5.
    Write-Host $_
    }
    write-host "after1"
    
    # Cancels whole script at 15, "after2" not printed.
    11..20 | ForEach-Object {
    if ($_ -eq 15) {continue}
    Write-Host $_
    }
    write-host "after2"
    
    # Cancels whole script at 25, "after3" not printed.
    21..30 | ForEach-Object {
    if ($_ -eq 25) {break}
    Write-Host $_
    }
    write-host "after3"
    

    foreach

    # Ends foreach at 5.
    foreach ($number1 in (1..10)) {
    if ($number1 -eq 5) {break}
    Write-Host "$number1"
    }
    write-host "after1"
    
    # Omit 15. 
    foreach ($number2 in (11..20)) {
    if ($number2 -eq 15) {continue}
    Write-Host "$number2"
    }
    write-host "after2"
    
    # Cancels whole script at 25, "after3" not printed.
    foreach ($number3 in (21..30)) {
    if ($number3 -eq 25) {return}
    Write-Host "$number3"
    }
    write-host "after3"
    
    0 讨论(0)
  • 2020-11-27 21:07

    If you insist on using ForEach-Object, then I would suggest adding a "break condition" like this:

    $Break = $False;
    
    1,2,3,4 | Where-Object { $Break -Eq $False } | ForEach-Object {
    
        $Break = $_ -Eq 3;
    
        Write-Host "Current number is $_";
    }
    

    The above code must output 1,2,3 and then skip (break before) 4. Expected output:

    Current number is 1
    Current number is 2
    Current number is 3
    
    0 讨论(0)
  • 2020-11-27 21:10

    Item #1. Putting a break within the foreach loop does exit the loop, but it does not stop the pipeline. It sounds like you want something like this:

    $todo=$project.PropertyGroup 
    foreach ($thing in $todo){
        if ($thing -eq 'some_condition'){
            break
        }
    }
    

    Item #2. PowerShell lets you modify an array within a foreach loop over that array, but those changes do not take effect until you exit the loop. Try running the code below for an example.

    $a=1,2,3
    foreach ($value in $a){
      Write-Host $value
    }
    Write-Host $a
    

    I can't comment on why the authors of PowerShell allowed this, but most other scripting languages (Perl, Python and shell) allow similar constructs.

    0 讨论(0)
  • 2020-11-27 21:18

    To stop the pipeline of which ForEach-Object is part just use the statement continue inside the script block under ForEach-Object. continue behaves differently when you use it in foreach(...) {...} and in ForEach-Object {...} and this is why it's possible. If you want to carry on producing objects in the pipeline discarding some of the original objects, then the best way to do it is to filter out using Where-Object.

    0 讨论(0)
  • 2020-11-27 21:19

    Below is a suggested approach to Question #1 which I use if I wish to use the ForEach-Object cmdlet. It does not directly answer the question because it does not EXIT the pipeline. However, it may achieve the desired effect in Q#1. The only drawback an amateur like myself can see is when processing large pipeline iterations.

        $zStop = $false
        (97..122) | Where-Object {$zStop -eq $false} | ForEach-Object {
        $zNumeric = $_
        $zAlpha = [char]$zNumeric
        Write-Host -ForegroundColor Yellow ("{0,4} = {1}" -f ($zNumeric, $zAlpha))
        if ($zAlpha -eq "m") {$zStop = $true}
        }
        Write-Host -ForegroundColor Green "My PSVersion = 5.1.18362.145"
    

    I hope this is of use. Happy New Year to all.

    0 讨论(0)
提交回复
热议问题