Loop range with steps
Asked Answered
C

3

11

I'm trying to move from BATCH into PowerShell and I am trying to convert my own scripts.

My problem is with ranges in loop: my original BATCH script was like

   for /L %%U in (123,2,323) do ECHO %%U

and will print

123
125
127
...

with Powershell a range would be 123..323, thus

123..323 | % {Echo $_ }

Will give me

123
124
125
...

Is there a way to set a range with a step that is different than 1? All the examples I find either list all the numbers (I have hundreds...) or use the two points between the numbers.

Caramelize answered 14/6, 2018 at 4:0 Comment(2)
#33892674Persephone
Thanks, I could not find this example while searching, now I will test all suggestionsCaramelize
S
13

Simple Google search should have found you this.

for ($i=123; $i -le 323; $i=$i+2 ) {Write-Host $i;}

(Initialize; condition to keep the loop running; iteration)

Seafowl answered 14/6, 2018 at 4:19 Comment(5)
Thank you, I suspect I could not find this solution because I was searching for the wrong thing. Now I will study how to pass the variable onward.Caramelize
@Squashman, you can also use +=2, (similarly to Set /A i+=2 with cmd.exe), e.g. For ($i=123; $i -LE 323; $i+=2) {$i}.Housman
@Seafowl This solution gave me the desired control over the steps (and was easy to understand since it was like counters in php) and I could successfully do a conversion of a couple of my existing BATCH scripts. Powershell ones proved to be 2x faster, so it was a needed improvement for my scripting!Caramelize
@Caramelize Powershell does do many tasks with less code then a batch-file, but there are still a lot of things that a batch-file can do more efficiently then PowerShell. There are things that I can write in a batch-file that has 10 times more lines of code but runs quicker then one line of code in Powershell.Seafowl
actually, google led me here. thank you for adding to the SO docsChuff
L
4

Using the original approach (Step 2):

(123..323) | % {if( $_ -band 1 ) {$_ }}

Step 3 and greater:

 (123..323) | % {if( !($i++ % 3) ) {$_ }} 
Lentha answered 14/6, 2018 at 4:54 Comment(4)
I hardly see a resemblance with an iterating for with start, step and end values, even if your approach has the same effect.Aboutship
Ok, in this case (123..323) | % {if( !($i++ % 3) ) {$_ }} is working better, but I like the approach with the fast binary operator.Lentha
I have problem in understanding how to change the step from 2 into another given number.Caramelize
See comment before.Lentha
R
0

A more concise way to create a range with a step value of n assuming variable $n contains the step value would be:

1..25 | ? { !($_ % $n) } 

The ? is an alias for the Where-Object cmdlet. And the $_ % $n can be written more explicitly as $_ % $n -eq 0 and hence the above can also be written as:

1..25 | Where-Object { $_ % $n -eq 0 }

Where-Object acts as a filter and only includes the values whose modulus with the step equals 0: $_ % $n -eq 0. The reason this can be written as !($_ % $n) is that PowerShell treats 0 as false and other numbers as true. So, negating the 0 with ! makes it evaluate to true and then causes the number to be included in the range.

So, for a step of 2 you would write:

123..323 | ? { !($_ % 2) }  

This includes only even numbers (124, 126, ...). To include only odd numbers, (starting from 123), a simple trick is:

123..323 | ? { $_ % 2 }

When the modulus evaluates to 1, which happens for an odd number, it is included in the final range.

Rollback answered 7/7, 2023 at 9:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.