2

I expect that the return from the function statement (begin, process, end) should also terminate all its statements, but this does not happen.

I have a test function like this:

function test($a) {
    begin { 
        Write-Host 'begin ' -NoNewline
        if ($a -like '*begin*') { return; } 
    }
    process {
        Write-Host 'process ' -NoNewline
        if ($a -like '*process*') { return; } 
    }
    end {
        Write-Host 'end' -NoNewline
        if ($a -like '*end*') { return; } 
    }
}

And when I run it this way:

'begin', 'process', 'end' | ForEach-Object {
    Write-Host "Try exit in $_ :`t" -NoNewline
    test $_
    Write-Host
}

Then I get a similar result:

Try exit in begin :     begin process end
Try exit in process :   begin process end
Try exit in end :       begin process end

But I would like the output from the function to also terminate its other statements and the output would be like this:

Try exit in begin :     begin
Try exit in process :   begin process
Try exit in end :       begin process end

How can I get it?

I tryed to use break, continue and exit instead of return but it terminated all script..

groser
  • 319
  • 1
  • 11
  • https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_functions_advanced_methods?view=powershell-7.1 – Colyn1337 Apr 02 '21 at 20:23
  • Try to write code in a block to avoid this problem. https://stackoverflow.com/a/73411227/9631497 – ZSkycat Aug 19 '22 at 02:30

1 Answers1

5

Your function is an advanced function, and therefore uses begin, process and end blocks.

In a simple (non-advanced) function, in the absence of such blocks, return would indeed instantly exit the function, but from inside these blocks, return just exits that block.

(As an aside: break and continue only apply to loop-like constructs, and exit is meant to exit scripts as a whole, not individual functions).


If you want to exit your advanced function as a whole you have two basic choices:

  • Throw an error, using a throw statement, which creates a script-terminating error (fatal by default).

  • Use a custom [bool] variable that ignores attempts to (re)enter the process / the other blocks.

    • Note that in order to exit the per-pipeline-input-object process block efficiently, it would be helpful to be able to instruct upstream commands that no more input should be provided. However, this isn't an option as of PowerShell 7.2, but has been proposed as a future enhancement in GitHub issue #3821.

To illustrate the technique of using a [bool] variable:

function test($a) {
  begin { 
    $done = $false
    Write-Host 'begin ' -NoNewline
    if ($a -like '*begin*') { $done = $true; return } 
  }
  process {
    if ($done) { return }
    Write-Host 'process ' -NoNewline
    if ($a -like '*process*') { $done = $true; return } 
  }
  end {
    if ($done) { return }
    Write-Host 'end' -NoNewline
    if ($a -like '*end*') { return } 
  }
}
mklement0
  • 382,024
  • 64
  • 607
  • 775