Recently, I've been playing with PowerShell, and I've noticed some weird behavior when using pipes and foreach loops that I couldn't understand.
This simple code works:
$x = foreach ($i in gci){$i.length}
$x | measure -max
Makes sense.
But this code won't:
foreach ($i in gci){$i.length} | measure -max
And I'm getting the following error:
An empty pipe element is not allowed.
At line:1 char:33
+ foreach ($i in gci){$i.length} | <<<< measure -max
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : EmptyPipeElement
What is the difference between those two methods, and why does the second one fails?
The foreach statement doesn't use the pipeline architecture, so its output cannot be passed to a pipeline directly (i.e. item by item). To be able to pass output from a foreach loop to a pipeline you must run the loop in a subexpression:
$(foreach ($item in Get-ChildItem) { $item.Length }) | ...
or collect it in a variable first:
$len = foreach ($item in Get-ChildItem) { ... }
$len | ...
If you want to process data in a pipeline use the ForEach-Object cmdlet instead:
Get-ChildItem | ForEach-Object { $_.Length } | ...
For further explanation of the differences between foreach statement and ForEach-Object cmdlet see the Scripting Guy blog and the chapter on loops from Master-PowerShell.
You need to evaluate the foreach before piping the resulting Object like you did in the first test:
$(foreach ($i in gci){$i.length}) | measure -max
Alternatively, use the % shorthand to which will evaluate it before piping it as well:
gci | % { $_.Length } | measure -max
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With