locked
Powershell Pipe & C# yield RRS feed

  • Question

  • Hi,

    On the thread :

    https://social.technet.microsoft.com/Forums/windowsserver/en-US/ca711409-adca-4835-920b-1909ce98fe44/c-style-yield-return-in-powershell?forum=winserverpowershell

    The guy ask if the yield exists in PowerShell. The answer is : PowerShell pipeline works like this.

     

    The case

     

    Three scenarios :

    • $results = Get-SomethingReallyBig
      $results | Format-Table

    and

    • Get-SomethingReallyBig | Format-Table

    and

    • (Get-SomethingReallyBig) | Format-Table

    I test the 3 solutions and the result is : the first and the third is the same, but not the second which displays on the fly.

     

    My question:

    I can understand the difference with the first one, because scripts are "step by step", but does it mean that the "( )" blocks the sequence and tell to PowerShell to finish the parenthesis task before passing the whole object to the pipeline ?

     

    It seems logical at the first view, but if I write :

    • 1,2,3 | Use-Myfunction
    • (1,2,3) | Use-Myfunction
    • Use-Myfunction -InputObject 1,2,3
    • Use-Myfunction -InputObject (1,2,3)

    If the advanced function is correctly written, the result should be the same, no ?



    Thursday, August 17, 2017 10:02 AM

Answers

  • However, that only means it'll evaluate (and execute) the logic inside. It has no effect on how the output of the parenthesis is treated. Thus:

    1,2,3 | Do-Something
    (1,2,3) | Do-Something

    are perfectly equivalent. On the other hand, thos two are not:

    (Get-Content .\largefile.txt) | Write-Host
    Get-Content .\largefile.txt | Write-Host

    Here, in the first line it will read all of the large file into memory and then pass along the objects, while in the second line it'll pass them as they are read (leading to a significantly lower memory footprint).

    These also are very different from each other:

    1,2,3 | Do-Something
    Do-SOmething -InputObject 1,2,3

    The difference here however is how Do-Something is being executed, something that can be equalized by sound coding.

    Cheers,
    Fred


    There's no place like 127.0.0.1

    • Proposed as answer by BOfH-666 Thursday, August 17, 2017 11:55 AM
    • Marked as answer by arnaud.helin Thursday, August 17, 2017 9:09 PM
    Thursday, August 17, 2017 11:45 AM

All replies

  • If you place something in parenthesis Powershell will first evaluate what's inside and then outputs the result. So - yes - it kind of "blocks" the pipeline for as long as it takes to run the code inside the parenthesis.

    Grüße - Best regards

    PS:> (79,108,97,102|%{[char]$_})-join''

    Thursday, August 17, 2017 11:29 AM
  • However, that only means it'll evaluate (and execute) the logic inside. It has no effect on how the output of the parenthesis is treated. Thus:

    1,2,3 | Do-Something
    (1,2,3) | Do-Something

    are perfectly equivalent. On the other hand, thos two are not:

    (Get-Content .\largefile.txt) | Write-Host
    Get-Content .\largefile.txt | Write-Host

    Here, in the first line it will read all of the large file into memory and then pass along the objects, while in the second line it'll pass them as they are read (leading to a significantly lower memory footprint).

    These also are very different from each other:

    1,2,3 | Do-Something
    Do-SOmething -InputObject 1,2,3

    The difference here however is how Do-Something is being executed, something that can be equalized by sound coding.

    Cheers,
    Fred


    There's no place like 127.0.0.1

    • Proposed as answer by BOfH-666 Thursday, August 17, 2017 11:55 AM
    • Marked as answer by arnaud.helin Thursday, August 17, 2017 9:09 PM
    Thursday, August 17, 2017 11:45 AM
  • I am a little bit confused.

    1) (Get-Content .\largefile.txt) | Write-Host
    2) Get-Content .\largefile.txt | Write-Host

    The first one load the whole object in the memory and passes it in one time through the pipeline. Then,

    does it mean that the second syntax do the following steps :

    1. Get the content of the file in memory
    2. Read the first line
    3. Passing the line to the pipeline
    4. Run the pipeline process (here -> Write-Host)
    5. Read the second line
    6. ...

    PowerShell loops on the step 2 to 4 until eof. Is it right ? Or there is something I'm wrong.

    If I'am right, is there any obvious case where the result should be very different ?

    Thursday, August 17, 2017 3:43 PM
  • Hi Arnaud,

    something like that. You can easily test it like this:

    function Get-Test
    {
    	[CmdletBinding()]
    	Param (
    		[Parameter(ValueFromPipeline = $true)]
    		$InputObject,
    		
    		[int]
    		$Stage = 0,
    		
    		[int]
    		$Wait = 1
    	)
    	
    	process
    	{
    		foreach ($item in $InputObject)
    		{
    			Write-Host "[$(Get-Date -Format "HH:mm:ss")] [$Stage] $InputObject"
    			Start-Sleep -Seconds $Wait
    			$InputObject
    		}
    	}
    }
    "a","b","c" | Get-Test -Stage 1 -Wait 1 | Get-Test -Stage 2 -Wait 5
    ("a","b","c" | Get-Test -Stage 1 -Wait 1) | Get-Test -Stage 2 -Wait 5

    Which way you want to go is really a matter of priorities. Piping is very good for the memory and less so (but usually not catastrophically so) for CPU.

    Cheers,
    Fred


    There's no place like 127.0.0.1


    • Edited by FWN Thursday, August 17, 2017 4:17 PM
    Thursday, August 17, 2017 4:15 PM
  • Thanks a lot, I tried your script and compared results. I understand now the pipeline behavior differences.
    Thursday, August 17, 2017 8:41 PM