Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to have index variable increase in ForEach-Object?

I have this strange observation:

I want to run an expression for as many times as there are files.

Yet, the following script always executes only once, no matter how many files are found by Get-ChildItem:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

(Get-ChildItem -Filter '*.png'), (Get-ChildItem -Filter '*.jpg') |
  Sort-Object -Property 'Name' |
  ForEach-Object -Begin { $idx = 0 } -Process { ++$idx; $idx; }

If I replace the expression with $_, all rows are returned as expected:

(Get-ChildItem -Filter '*.png'), (Get-ChildItem -Filter '*.jpg') |
  Sort-Object -Property 'Name' |
  ForEach-Object -Begin { $idx = 0 } -Process { $_; }

>Solution :

As Mathias points out, (...), (...) creates a nested array, which is not your intent (the , operator constructs an array from its operands, even if those operands are themselves arrays).

The best way to provide output from multiple commands as pipeline input is to use & (or . , if you need the commands to run directly in the caller’s scope) with a script block ({ ... }), in which, as usual you can separate commands with ;:

& { Get-ChildItem -Filter *.png; Get-ChildItem -Filter *.jpg } |
  Sort-Object -Property Name |
  ForEach-Object -Begin { $idx = 0 } -Process { ++$idx; $idx; }

This approach streams the command output, whereas use of $(...) or @(...) (which in this particular case can be used interchangeably) – $(Get-ChildItem -Filter *.png; Get-ChildItem -Filter *.jpg) or @(Get-ChildItem -Filter *.png; Get-ChildItem -Filter *.jpg)collect all output first and then send it to the pipeline.


An simplified version of your command that makes do with a single Get-ChildItem call, using the -Path parameter’s support for multiple wildcard patterns:

Get-ChildItem -Path *.png, *.jpg |
  Sort-Object -Property Name |
  ForEach-Object -Begin { $idx = 0 } -Process { (++$idx) }
  • -Filter is usually preferable, because it filters at the source, but it only supports one wildcard pattern at a time; while -Path, which makes PowerShell filter the results is noticeably slower, the overhead from an extra Get-ChildItem call may negate that advantage.

    • There’s also -Include / -Exclude, which also filter on the PowerShell side, but, unfortunately, they do not work as one would intuitively expect: see this answer.
  • Also note the use of (...) around ++$idx, which causes the updated value to also be output.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading