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