Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

pipes and foreach loops

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?

like image 681
Jellybean Avatar asked Sep 10 '15 23:09

Jellybean


2 Answers

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.

like image 63
Ansgar Wiechers Avatar answered Oct 24 '22 09:10

Ansgar Wiechers


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
like image 1
xXhRQ8sD2L7Z Avatar answered Oct 24 '22 08:10

xXhRQ8sD2L7Z