4

I am trying to pipe the output from a foreach loop into a format command but it does not work. The reason I think is possible is because this works.

$op = foreach ($file in (Get-ChildItem -File)) {
    $file |
    Get-Member |
    Where-Object {$_.MemberType -eq "Method" -and $_.Definition -like "*system*" } |
    Select-Object -Property Name, MemberType
}

$op | Format-List

If I can assign the whole output to a variable, and pipe the variable into another command, why does the following NOT work?

(foreach ($file in (Get-ChildItem -File)) {
    $file |
    Get-Member |
    Where-Object {$_.MemberType -eq "Method" -and $_.Definition -like "*system*" } |
    Select-Object -Property Name, MemberType
}) | Format-List

Of course I tried without parents, but ultimately I think if anything, the parents make sense. It is like $file in (Get-ChildItem -File) where it evaluates the expression in the parents and uses the result as the actual object

Is there a way to make this work?

please note that the code is not supposed to achieve anything (else) than giving an example of the mechanics

5
  • 1
    Powershell distinguishes between Command parsing mode and Expression parsing mode - you've got both cases... Commented Nov 20, 2019 at 18:17
  • 1
    If you use $(foreach ...), it'll work. Commented Nov 20, 2019 at 18:22
  • 1
    I don't know the exact reason or differences between $() and (). I've asked questions to this tag in the past, but no one was able to give me a concise answer. I just know wrapping things in a subexpression causes them to output differently. Commented Nov 20, 2019 at 18:24
  • 2
    It's explained here... Commented Nov 20, 2019 at 19:56
  • @PeterSchneider Very nice. Your google-fu is stronger than mine Commented Nov 20, 2019 at 20:18

2 Answers 2

3

foreach does not have an output you can capture (besides the sugar you've found with variable assignment), but you can gather all the objects returned by wrapping it in a subexpression:

$(foreach ($file in Get-ChildItem -File) {
    # ...
}) | Format-List

This same pattern can be used for if and switch statements as well.

Sign up to request clarification or add additional context in comments.

Comments

2

Here's another way to do it, without waiting for the whole foreach to finish. It's like defining a function on the fly:

& { foreach ($file in Get-ChildItem -File) {
      $file |
      Get-Member |
      Where-Object {$_.MemberType -eq "Method" -and $_.Definition -like "*system*" } |
      Select-Object -Property Name, MemberType
    } 
} | format-list

By the way, $( ) can go anywhere ( ) can go, but it can enclose multiple statements separated by newlines or semicolons.

Also, you can pipe it directly:

Get-ChildItem -File |
Get-Member |
Where-Object {$_.MemberType -eq "Method" -and $_.Definition -like "*system*" } |
Select-Object -Property Name, MemberType | 
Format-List

4 Comments

Ah, nice use of IIFE. Forgot you could do that. It just has extra overhead of creating a new scope and managing invocation
ok, nice! I knew this technique but a bit different. 1..3 | & {Process { $_*3 }} It makes sense now. I learned yesterday that a function will by default use the end block. That is why in my example here I need process explicitly but when you use it, the end block is correct and you return the result which is pipable. But why does it not need to wait for the foreach to finish?
I don't know why. I just know that it does.
@TheFool pipeline and scriptblock black magic; it's not seen as a single command like a subexpression

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.