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.
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.
1..100MB for instance. Compare with [System.Linq.Enumerable]::Range(0, 100MB) which starts to enumerate immediately. I wonder why PowerShell choose that design.foreach( $i in 0..100MB ) { $i } effectively streams is surprising, given that streaming command output is collected in full, up front in foreach statements; e.g.: foreach ($f in Get-ChildItem $HOME -File -Recurse -ErrorAction Ignore) { $f; pause } Therefore, in the case at hand, if a Get-ChildItem call determines the number of sequence numbers to produce, using the pipeline with ForEach-Object is your only option if you want to avoid a delay stemming from up-front collection.0..[int]::MaxValue will instantly throw but foreach($i in 0..[int]::MaxValue) { $i } enumerates([int]::MinValue)..([int]::MaxValue) | % { $_; pause }foreach enumerates ranges: foreach($i in 0..0) { $foreach.GetType().Name } prints RangeEnumerator. Compare with foreach($i in (0..0)) { $foreach.GetType().Name } which prints ArrayEnumerator.
$PSIndexvariable. :)