3

My powershell module has a function and I want it to return a non zero exit code. However, being a module function it is loaded into the context of the powershell console when I run Import-Module. So, when the function executes exit 1 - poof, goes the console window!

At least, this is how I explain it closing the powershell window when it exits.

So, how can a ps module function exit with a non zero exit code without killing the console where the module was imported?

P.S.

I did notice several questions on SO about this subject, but none seems to examine this particular case.

EDIT 1

I would like to provide some context. I have a PS module with a lot of functions. Some of them are used as is in Azure DevOps yaml build scripts. The latter knows to recognize non zero exit code and abort the pipeline, so it is not necessary to throw from a function to abort the flow.

However, if I want to call that function from the console, e.g. to test something quickly and it exits with non zero code, the whole console window is closed. This is extremely annoying.

Sometimes there is a workaround. So, instead of this code:

dotnet build ...
$ExitCode = $LastExitCode
DoSomethingInAnyCase()
if ($ExitCode)
{
    exit $ExitCode
}

We can have the following version:

try
{
    dotnet build ...
}
finally
{
    DoSomethingInAnyCase()
}

Both versions would correctly return the right exit code, but because the second one does not have the explicit exit statement, it does not close the console.

11
  • What problem are you trying to solve (by needing a non-zero exit code)? Commented Nov 21, 2019 at 16:29
  • I want to indicate a failure without explicit throw statement. Commented Nov 21, 2019 at 18:04
  • Why? What's wrong with throwing an exception? Commented Nov 21, 2019 at 18:50
  • Too much garbage on the screen as a result of it. I do use it often, but I want to know about exit codes, hence the question. Commented Nov 21, 2019 at 19:04
  • I'm trying to understand your goal. Modules implement functions that execute in the current scope. Functions don't change exit codes because those are only set by processes or scripts. Although I guess you could write & $Env:ComSpec /c exit n? Commented Nov 21, 2019 at 19:09

2 Answers 2

6

You'll have to set $global:LASTEXITCODE in order to set the exit code, but note that PowerShell functions aren't really meant to set exit codes, only scripts, and the latter only for reporting exit codes to the outside world, via the PowerShell process' own exit code, when PowerShell is called via its (CLI powershell.exe for Windows PowerShell, pwsh for PowerShell Core) from a build tool, scheduled task, or another shell, for instance.

Also note that setting $global:LASTEXITCODE directly:

  • does not make $?, the automatic success-status variable, reflect $false in the caller's context, the way that exit <nonzero-value> does from a script and the way that calling an external program that reports a nonzero exit code does.

  • is not enough to make the PowerShell process as a whole report this exit code.

In short: All this gains you is that the caller can inspect $LASTEXITCODE after your function was called, as you would after calling an external program.


Generally, exit codes are an inter-process concept and do not fit well into PowerShell's in-process world.

For more information about exit codes in PowerShell, see this post.


PowerShell's analog to exit codes is $?, the automatic, Boolean success-status variable ($true or $false), which reflects a PowerShell command's success immediately afterwards.
(If that command is an external-program call, an exit code of 0 sets $? to $true, and any nonzero one sets it to $false).

As it turns out, setting that was what you really meant to ask.

As of PowerShell Core 7.0.0-preview.5, you cannot set $? directly.

For now, these are the only ways to cause $? to reflect $false in the caller's scope, and, conversely, you cannot always ensure that it is $true:

  • From a script: Exit the script with exit <nonzero-integer>

  • From a cmdlet or function (script as well):

    • Throw a script-terminating error (Throw) or a statement-terminating error ($PSCmdlet.ThrowTerminatingError()) - the latter being only available in advanced functions and scripts.

    • Write an error to PowerShell's error stream with $PSCmdlet.WriteError() - the latter being only available in advanced functions and scripts.

      • Note that this unexpectedly currently does not apply to the Write-Error cmdlet - see this GitHub issue

Note that both techniques invariably involve emitting an error.

Since it sounds like that's precisely what you're trying to avoid, you'll have to wait until the ability to set $? directly is implemented.

The decision to implement this ability has been made, but it's unclear when it will be implemented.

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

Comments

1

Workaround: Run cmd.exe and set whatever exit code you want before exiting the function. Example:

function Test-Function {
  $cmd = Join-Path ([Environment]::GetFolderPath([Environment+SpecialFolder]::System)) "cmd.exe"
  & $cmd /c exit 3
  # $LASTEXITCODE will be set to 3
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.