Search code examples
powershellindexingget-childitemforeach-object

Using an Index with foreach-object in powershell


I have this code:

Get-ChildItem FOLDERNAMEHERE *.png| ForEach-Object { $_.Name } > fileNames.txt

It prints off a list of file Names, and I want to change it to out just print out an index of numbers instead of Names.


Solution

  • To output sequence numbers starting with 1 (replace $i = 0 with $i = -1 to start with 0):

    Get-ChildItem FOLDERNAMEHERE *.png |
      ForEach-Object -Begin { $i = 0 } -Process { (++$i) }
    

    Note that the variable $i lives on after this command, and, if it preexisted before running the command, effectively changes it, because the script blocks passed to ForEach-Object run directly in the caller's scope.

    The increment assignment operation (++) is wrapped in (...) so as to also output the incremented value (assignment statements produce no output by default).

    As an aside:

    • GitHub issue #13772 proposes introducing an automatic index variable for use in pipelines, such as $PSIndex, which would simplify the solution to:

      Get-ChildItem FOLDERNAMEHERE *.png | ForEach-Object { $PSIndex + 1 }
      

    Non-streaming alternative, using .., the range operator:

    if ($count = (Get-ChildItem FOLDERNAMEHERE *.png).Count) {
      1..$count
    }
    

    Note: Non-streaming means that the results of the Get-ChildItem call are collected in full, up front in order to determine the upper limit for the sequence numbers.

    Similarly, the 1..$count range operation collects the elements between the range endpoints in an array up front before beginning to produce visible output. See the comments for a discussion.

    In other words: Go with the streaming, ForEach-Object-based solution if the Get-ChildItem command produces a lot of output objects and you want to start emitting sequence numbers as quickly as possible.