Break out of inner loop only in nested loop - powershell

I'm trying to implement a break so I don't have to continue to loop when I got the result xxxxx times.
$baseFileCsvContents | ForEach-Object {
# Do stuff
$fileToBeMergedCsvContents | ForEach-Object {
If ($_.SamAccountName -eq $baseSameAccountName) {
# Do something
break
}
# Stop doing stuff in this For Loop
}
# Continue doing stuff in this For Loop
}
The problem is, that break is exiting both ForEach-Object loops, and I just want it to exit the inner loop. I have tried reading and setting flags like :outer, however all I get is syntax errors.
Anyone know how to do this?

You won't be able to use a named loop with ForEach-Object, but can do it using the ForEach keyword instead like so:
$OuterLoop = 1..10
$InnerLoop = 25..50
$OuterLoop | ForEach-Object {
Write-Verbose "[OuterLoop] $($_)" -Verbose
:inner
ForEach ($Item in $InnerLoop) {
Write-Verbose "[InnerLoop] $($Item)" -Verbose
If ($Item -eq 30) {
Write-Warning 'BREAKING INNER LOOP!'
BREAK inner
}
}
}
Now whenever it gets to 30 on each innerloop, it will break out to the outer loop and continue on.

Using the return keyword works, as "ForEach-Object" takes a scriptblock as it's parameter and than invokes that scriptblock for every element in the pipe.
1..10 | ForEach-Object {
$a = $_
1..2 | ForEach-Object {
if($a -eq 5 -and $_ -eq 1) {return}
"$a $_"
}
"---"
}
Will skip "5 1"

Related

Why my code is not executed after the "break" when it is outside the loop? [duplicate]

I have the following code:
$project.PropertyGroup | Foreach-Object {
if($_.GetAttribute('Condition').Trim() -eq $propertyGroupConditionName.Trim()) {
$a = $project.RemoveChild($_);
Write-Host $_.GetAttribute('Condition')"has been removed.";
}
};
Question #1: How do I exit from ForEach-Object? I tried using "break" and "continue", but it doesn't work.
Question #2: I found that I can alter the list within a foreach loop... We can't do it like that in C#... Why does PowerShell allow us to do that?
First of all, Foreach-Object is not an actual loop and calling break in it will cancel the whole script rather than skipping to the statement after it.
Conversely, break and continue will work as you expect in an actual foreach loop.
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.
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"
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.
Since ForEach-Object is a cmdlet, break and continue will behave differently here than with the foreach keyword. Both will stop the loop but will also terminate the entire script:
break:
0..3 | foreach {
if ($_ -eq 2) { break }
$_
}
echo "Never printed"
# OUTPUT:
# 0
# 1
continue:
0..3 | foreach {
if ($_ -eq 2) { continue }
$_
}
echo "Never printed"
# OUTPUT:
# 0
# 1
So far, I have not found a "good" way to break a foreach script block without breaking the script, except "abusing" exceptions, although powershell core uses this approach:
throw:
class CustomStopUpstreamException : Exception {}
try {
0..3 | foreach {
if ($_ -eq 2) { throw [CustomStopUpstreamException]::new() }
$_
}
} catch [CustomStopUpstreamException] { }
echo "End"
# OUTPUT:
# 0
# 1
# End
The alternative (which is not always possible) would be to use the foreach keyword:
foreach:
foreach ($_ in (0..3)) {
if ($_ -eq 2) { break }
$_
}
echo "End"
# OUTPUT:
# 0
# 1
# End
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
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.
There is a way to break from ForEach-Object without throwing an exception. It employs a lesser-known feature of Select-Object, using the -First parameter, which actually breaks the pipeline when the specified number of pipeline items have been processed.
Simplified example:
$null = 1..5 | ForEach-Object {
# Do something...
Write-Host $_
# Evaluate "break" condition -> output $true
if( $_ -eq 2 ) { $true }
} | Select-Object -First 1 # Actually breaks the pipeline
Output:
1
2
Note that the assignment to $null is there to hide the output of $true, which is produced by the break condition. The value $true could be replaced by 42, "skip", "foobar", you name it. We just need to pipe something to Select-Object so it breaks the pipeline.
I found this question while looking for a way to have fine grained flow control to break from a specific block of code. The solution I settled on wasn't mentioned...
Using labels with the break keyword
From: about_break
A Break statement can include a label that lets you exit embedded
loops. A label can specify any loop keyword, such as Foreach, For, or
While, in a script.
Here's a simple example
:myLabel for($i = 1; $i -le 2; $i++) {
Write-Host "Iteration: $i"
break myLabel
}
Write-Host "After for loop"
# Results:
# Iteration: 1
# After for loop
And then a more complicated example that shows the results with nested labels and breaking each one.
:outerLabel for($outer = 1; $outer -le 2; $outer++) {
:innerLabel for($inner = 1; $inner -le 2; $inner++) {
Write-Host "Outer: $outer / Inner: $inner"
#break innerLabel
#break outerLabel
}
Write-Host "After Inner Loop"
}
Write-Host "After Outer Loop"
# Both breaks commented out
# Outer: 1 / Inner: 1
# Outer: 1 / Inner: 2
# After Inner Loop
# Outer: 2 / Inner: 1
# Outer: 2 / Inner: 2
# After Inner Loop
# After Outer Loop
# break innerLabel Results
# Outer: 1 / Inner: 1
# After Inner Loop
# Outer: 2 / Inner: 1
# After Inner Loop
# After Outer Loop
# break outerLabel Results
# Outer: 1 / Inner: 1
# After Outer Loop
You can also adapt it to work in other situations by wrapping blocks of code in loops that will only execute once.
:myLabel do {
1..2 | % {
Write-Host "Iteration: $_"
break myLabel
}
} while ($false)
Write-Host "After do while loop"
# Results:
# Iteration: 1
# After do while loop
You have two options to abruptly exit out of ForEach-Object pipeline in PowerShell:
Apply exit logic in Where-Object first, then pass objects to Foreach-Object, or
(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
Answer for Question #1 -
You could simply have your if statement stop being TRUE
$project.PropertyGroup | Foreach {
if(($_.GetAttribute('Condition').Trim() -eq $propertyGroupConditionName.Trim()) -and !$FinishLoop) {
$a = $project.RemoveChild($_);
Write-Host $_.GetAttribute('Condition')"has been removed.";
$FinishLoop = $true
}
};

PowerShell : Return keyword inside Foreach block not returning line of command to caller but acts as continue [duplicate]

I have the following code:
$project.PropertyGroup | Foreach-Object {
if($_.GetAttribute('Condition').Trim() -eq $propertyGroupConditionName.Trim()) {
$a = $project.RemoveChild($_);
Write-Host $_.GetAttribute('Condition')"has been removed.";
}
};
Question #1: How do I exit from ForEach-Object? I tried using "break" and "continue", but it doesn't work.
Question #2: I found that I can alter the list within a foreach loop... We can't do it like that in C#... Why does PowerShell allow us to do that?
First of all, Foreach-Object is not an actual loop and calling break in it will cancel the whole script rather than skipping to the statement after it.
Conversely, break and continue will work as you expect in an actual foreach loop.
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.
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"
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.
Since ForEach-Object is a cmdlet, break and continue will behave differently here than with the foreach keyword. Both will stop the loop but will also terminate the entire script:
break:
0..3 | foreach {
if ($_ -eq 2) { break }
$_
}
echo "Never printed"
# OUTPUT:
# 0
# 1
continue:
0..3 | foreach {
if ($_ -eq 2) { continue }
$_
}
echo "Never printed"
# OUTPUT:
# 0
# 1
So far, I have not found a "good" way to break a foreach script block without breaking the script, except "abusing" exceptions, although powershell core uses this approach:
throw:
class CustomStopUpstreamException : Exception {}
try {
0..3 | foreach {
if ($_ -eq 2) { throw [CustomStopUpstreamException]::new() }
$_
}
} catch [CustomStopUpstreamException] { }
echo "End"
# OUTPUT:
# 0
# 1
# End
The alternative (which is not always possible) would be to use the foreach keyword:
foreach:
foreach ($_ in (0..3)) {
if ($_ -eq 2) { break }
$_
}
echo "End"
# OUTPUT:
# 0
# 1
# End
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
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.
There is a way to break from ForEach-Object without throwing an exception. It employs a lesser-known feature of Select-Object, using the -First parameter, which actually breaks the pipeline when the specified number of pipeline items have been processed.
Simplified example:
$null = 1..5 | ForEach-Object {
# Do something...
Write-Host $_
# Evaluate "break" condition -> output $true
if( $_ -eq 2 ) { $true }
} | Select-Object -First 1 # Actually breaks the pipeline
Output:
1
2
Note that the assignment to $null is there to hide the output of $true, which is produced by the break condition. The value $true could be replaced by 42, "skip", "foobar", you name it. We just need to pipe something to Select-Object so it breaks the pipeline.
I found this question while looking for a way to have fine grained flow control to break from a specific block of code. The solution I settled on wasn't mentioned...
Using labels with the break keyword
From: about_break
A Break statement can include a label that lets you exit embedded
loops. A label can specify any loop keyword, such as Foreach, For, or
While, in a script.
Here's a simple example
:myLabel for($i = 1; $i -le 2; $i++) {
Write-Host "Iteration: $i"
break myLabel
}
Write-Host "After for loop"
# Results:
# Iteration: 1
# After for loop
And then a more complicated example that shows the results with nested labels and breaking each one.
:outerLabel for($outer = 1; $outer -le 2; $outer++) {
:innerLabel for($inner = 1; $inner -le 2; $inner++) {
Write-Host "Outer: $outer / Inner: $inner"
#break innerLabel
#break outerLabel
}
Write-Host "After Inner Loop"
}
Write-Host "After Outer Loop"
# Both breaks commented out
# Outer: 1 / Inner: 1
# Outer: 1 / Inner: 2
# After Inner Loop
# Outer: 2 / Inner: 1
# Outer: 2 / Inner: 2
# After Inner Loop
# After Outer Loop
# break innerLabel Results
# Outer: 1 / Inner: 1
# After Inner Loop
# Outer: 2 / Inner: 1
# After Inner Loop
# After Outer Loop
# break outerLabel Results
# Outer: 1 / Inner: 1
# After Outer Loop
You can also adapt it to work in other situations by wrapping blocks of code in loops that will only execute once.
:myLabel do {
1..2 | % {
Write-Host "Iteration: $_"
break myLabel
}
} while ($false)
Write-Host "After do while loop"
# Results:
# Iteration: 1
# After do while loop
You have two options to abruptly exit out of ForEach-Object pipeline in PowerShell:
Apply exit logic in Where-Object first, then pass objects to Foreach-Object, or
(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
Answer for Question #1 -
You could simply have your if statement stop being TRUE
$project.PropertyGroup | Foreach {
if(($_.GetAttribute('Condition').Trim() -eq $propertyGroupConditionName.Trim()) -and !$FinishLoop) {
$a = $project.RemoveChild($_);
Write-Host $_.GetAttribute('Condition')"has been removed.";
$FinishLoop = $true
}
};

Powershell: break from two or more loops

I have a script which pings Systems until it is terminated. The main code block is a function that has a foreach loop nested into a do until loop.
## Start
function start-skript{
Write-Output 'Ping läuft...'
$Repetition_Counter = 0
#Start loop
do {
#Host loop
foreach($system in $Hosts_Array) {
#Ping Function
Ping-Host -HostToPing $system
#Write-Host 'Sleep for' $Pause 'sekunde'
Start-Sleep -Seconds $Pause
} $Repetition_Counter ++
} until($Repetition_Counter -eq $Repetition -or (stop-script))
}
start-skript
It works finely. However, by invoking the stop-script function, the script can only be stopped, only after the function ping-host has been applied to all the devices in the $Hosts_Array. I wan to be able to stop the script by invoking the stop-script function, even if the funtion Ping-host hasn't applied to all the devices in the $Hosts_Array array . I thought, I could do like below
## Start
function start-skript{
Write-Output 'Ping läuft...'
$Repetition_Counter = 0
#Start loop
do {
#Host loop
foreach($system in $Hosts_Array) {
#Ping Function
Ping-Host -HostToPing $system
#Write-Host 'Sleep for' $Pause 'sekunde'
**stop-script** ## I've just added the function here
Start-Sleep -Seconds $Pause
} $Repetition_Counter ++
} until($Repetition_Counter -eq $Repetition -or (stop-script))
}
start-skript
I didn't work out. It just comes out of the foreach loop but then starts again with the foreach loop, bcz the foreach loop is into the do until loop..
Any suggestions??
Use a statement label:
:outer
do
{
:inner
foreach($i in 1..10){
if($i -eq 2){
continue inner
}
if($i -gt 5){
break outer
}
$i
}
}while($true)
Output will be:
1
3
4
5
Since we continue'd the inner loop on $i -eq 2 and broke out of the outer loop after $i -eq 5

PowerShell 6 Break from ForEach-Object Loop without exiting script [duplicate]

I have the following code:
$project.PropertyGroup | Foreach-Object {
if($_.GetAttribute('Condition').Trim() -eq $propertyGroupConditionName.Trim()) {
$a = $project.RemoveChild($_);
Write-Host $_.GetAttribute('Condition')"has been removed.";
}
};
Question #1: How do I exit from ForEach-Object? I tried using "break" and "continue", but it doesn't work.
Question #2: I found that I can alter the list within a foreach loop... We can't do it like that in C#... Why does PowerShell allow us to do that?
First of all, Foreach-Object is not an actual loop and calling break in it will cancel the whole script rather than skipping to the statement after it.
Conversely, break and continue will work as you expect in an actual foreach loop.
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.
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"
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.
Since ForEach-Object is a cmdlet, break and continue will behave differently here than with the foreach keyword. Both will stop the loop but will also terminate the entire script:
break:
0..3 | foreach {
if ($_ -eq 2) { break }
$_
}
echo "Never printed"
# OUTPUT:
# 0
# 1
continue:
0..3 | foreach {
if ($_ -eq 2) { continue }
$_
}
echo "Never printed"
# OUTPUT:
# 0
# 1
So far, I have not found a "good" way to break a foreach script block without breaking the script, except "abusing" exceptions, although powershell core uses this approach:
throw:
class CustomStopUpstreamException : Exception {}
try {
0..3 | foreach {
if ($_ -eq 2) { throw [CustomStopUpstreamException]::new() }
$_
}
} catch [CustomStopUpstreamException] { }
echo "End"
# OUTPUT:
# 0
# 1
# End
The alternative (which is not always possible) would be to use the foreach keyword:
foreach:
foreach ($_ in (0..3)) {
if ($_ -eq 2) { break }
$_
}
echo "End"
# OUTPUT:
# 0
# 1
# End
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
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.
There is a way to break from ForEach-Object without throwing an exception. It employs a lesser-known feature of Select-Object, using the -First parameter, which actually breaks the pipeline when the specified number of pipeline items have been processed.
Simplified example:
$null = 1..5 | ForEach-Object {
# Do something...
Write-Host $_
# Evaluate "break" condition -> output $true
if( $_ -eq 2 ) { $true }
} | Select-Object -First 1 # Actually breaks the pipeline
Output:
1
2
Note that the assignment to $null is there to hide the output of $true, which is produced by the break condition. The value $true could be replaced by 42, "skip", "foobar", you name it. We just need to pipe something to Select-Object so it breaks the pipeline.
I found this question while looking for a way to have fine grained flow control to break from a specific block of code. The solution I settled on wasn't mentioned...
Using labels with the break keyword
From: about_break
A Break statement can include a label that lets you exit embedded
loops. A label can specify any loop keyword, such as Foreach, For, or
While, in a script.
Here's a simple example
:myLabel for($i = 1; $i -le 2; $i++) {
Write-Host "Iteration: $i"
break myLabel
}
Write-Host "After for loop"
# Results:
# Iteration: 1
# After for loop
And then a more complicated example that shows the results with nested labels and breaking each one.
:outerLabel for($outer = 1; $outer -le 2; $outer++) {
:innerLabel for($inner = 1; $inner -le 2; $inner++) {
Write-Host "Outer: $outer / Inner: $inner"
#break innerLabel
#break outerLabel
}
Write-Host "After Inner Loop"
}
Write-Host "After Outer Loop"
# Both breaks commented out
# Outer: 1 / Inner: 1
# Outer: 1 / Inner: 2
# After Inner Loop
# Outer: 2 / Inner: 1
# Outer: 2 / Inner: 2
# After Inner Loop
# After Outer Loop
# break innerLabel Results
# Outer: 1 / Inner: 1
# After Inner Loop
# Outer: 2 / Inner: 1
# After Inner Loop
# After Outer Loop
# break outerLabel Results
# Outer: 1 / Inner: 1
# After Outer Loop
You can also adapt it to work in other situations by wrapping blocks of code in loops that will only execute once.
:myLabel do {
1..2 | % {
Write-Host "Iteration: $_"
break myLabel
}
} while ($false)
Write-Host "After do while loop"
# Results:
# Iteration: 1
# After do while loop
You have two options to abruptly exit out of ForEach-Object pipeline in PowerShell:
Apply exit logic in Where-Object first, then pass objects to Foreach-Object, or
(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
Answer for Question #1 -
You could simply have your if statement stop being TRUE
$project.PropertyGroup | Foreach {
if(($_.GetAttribute('Condition').Trim() -eq $propertyGroupConditionName.Trim()) -and !$FinishLoop) {
$a = $project.RemoveChild($_);
Write-Host $_.GetAttribute('Condition')"has been removed.";
$FinishLoop = $true
}
};

What is the cleanest way to join in one array the result of two or more calls to Get-ChildItem?

I'm facing the problem of moving and copying some items on the file system with PowerShell.
I know by experiments the fact that, even with PowerShell v3, the cmdlet Copy-Item, Move-Item and Delete-Item cannot handle correctly reparse point like junction and symbolic link, and can lead to disasters if used with switch -Recurse.
I want to prevent this evenience. I have to handle two or more folder each run, so I was thinking to something like this.
$Strings = #{ ... }
$ori = Get-ChildItem $OriginPath -Recurse
$dri = Get-ChildItem $DestinationPath -Recurse
$items = ($ori + $dri) | where { $_.Attributes -match 'ReparsePoint' }
if ($items.Length -gt 0)
{
Write-Verbose ($Strings.LogExistingReparsePoint -f $items.Length)
$items | foreach { Write-Verbose " $($_.FullName)" }
throw ($Strings.ErrorExistingReparsePoint -f $items.Length)
}
This doen't work because $ori and $dri can be also single items and not arrays: the op-Addition will fail. Changing to
$items = #(#($ori) + #($dri)) | where { $_.Attributes -match 'ReparsePoint' }
poses another problem because $ori and $dri can also be $null and I can end with an array containing $null. When piping the join resutl to Where-Object, again, I can end with a $null, a single item, or an array.
The only apparently working solution is the more complex code following
$items = $()
if ($ori -ne $null) { $items += #($ori) }
if ($dri -ne $null) { $items += #($dri) }
$items = $items | where { $_.Attributes -match 'ReparsePoint' }
if ($items -ne $null)
{
Write-Verbose ($Strings.LogExistingReparsePoint -f #($items).Length)
$items | foreach { Write-Verbose " $($_.FullName)" }
throw ($Strings.ErrorExistingReparsePoint -f #($items).Length)
}
There is some better approch?
I'm interested for sure if there is a way to handle reparse point with PowerShell cmdlets in the correct way, but I'm much more interested to know how to join and filters two or more "PowerShell collections".
I conclude observing that, at present, this feature of PowerShell, the "polymorphic array", doen't appear such a benefit to me.
Thanks for reading.
Just add a filter to throw out nulls. You're on the right track.
$items = #(#($ori) + #($dri)) | ? { $_ -ne $null }
I've been on Powershell 3 for a while now but from what I can tell this should work in 2.0 as well:
$items = #($ori, $dri) | %{ $_ } | ? { $_.Attributes -match 'ReparsePoint' }
Basically %{ $_ } is a foreach loop that unrolls the inner arrays by iterating over them and passing each inner element ($_) down the pipeline. Nulls will automatically be excluded from the pipeline.