Question:
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:
1 2 3 |
$x = foreach ($i in gci){$i.length} $x | measure -max |
Makes sense.
But this code won’t:
1 2 |
foreach ($i in gci){$i.length} | measure -max |
And I’m getting the following error:
1 2 3 4 5 6 |
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?
Answer:
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:
1 2 |
$(foreach ($item in Get-ChildItem) { $item.Length }) | ... |
or collect it in a variable first:
1 2 3 |
$len = foreach ($item in Get-ChildItem) { ... } $len | ... |
If you want to process data in a pipeline use the ForEach-Object
cmdlet instead:
1 2 |
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.