Reputation: 16236
I have questions about returning an exit code value from PowerShell when run from a cmd.exe invocation. I found https://weblogs.asp.net/soever/returning-an-exit-code-from-a-powershell-script which has been helpful. But, the solution for PowerShell code is to add a function.
function ExitWithCode { param($exitcode) $host.SetShouldExit($exitcode) exit }
Generates:
ExitWithCode : The term 'ExitWithCode' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path
was included, verify that the path is correct and try again.
At C:\src\t\e\exit5.ps1:6 char:1
+ ExitWithCode -exitcode 12345
+ ~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (ExitWithCode:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
But, placing the "exit" on a new line works. Is this just a language anomaly?
function ExitWithCode { param($exitcode) $host.SetShouldExit($exitcode)
exit }
Also, this page is from 2010. Is this still the current state? Is there a better/easier way now?
Upvotes: 14
Views: 125566
Reputation: 437673
Guenther Schmitz's answer solves your immediate syntax problem, but it's important to note
that $host.SetShouldExit()
is not meant to be called by user code, as implied by Bruce Payette's answer.
Instead, it is used internally by PowerShell itself in response to an exit
statement in user code.
The only conceivable reason to use it is to repurpose it for implementing a workaround around a limitation of exit-code reporting when a script is being called via the -Command
(-c
) parameter of PowerShell's CLI:
With -Command
, a script's specific non-zero exit code is always translated to 1
, so the specific exit code is lost - see this answer for an overview of exit-code handling in PowerShell.
$host.SetShouldExit()
, despite not being intended for this purpose, happens to overcome this limitation and ensures that the script's exit code is also reported as the PowerShell process' exit code.
This workaround must not be applied when the script is being called from an interactive PowerShell session, because it will cause the session as a whole to exit instantly, which is the subject of your follow-up question.
Reliable detection of when a script is being called via -Command
is nontrivial and hard to make fully reliable, however, as shown in this answer to your follow-up question.
The better approach is not to use the $host.SetShouldExit()
workaround and do the following instead.
Invoke your script via the -File
CLI parameter, in which case no workaround is needed:
Just use exit $n
in your script, and $n
will be reported as the PowerShell process' exit code (assuming $n
is an integer).
When calling via -Command
, follow the script call with ; exit $LASTEXITCODE
in the command string so as to ensure that the script's exit code is passed through.
Of course, you may not always be in control of how your script is invoked via the CLI; in that event, the workaround is worth considering.
Upvotes: 19
Reputation: 200273
As Guenther Schmitz already explained, $host.SetShouldExit($exitcode)
and exit
are 2 distinct statements that must be separated either with a newline or a semicolon.
Without that separation your code should have thrown a different error, though:
Unexpected token 'exit' in expression or statement.
The error you posted looks more like you tried to use that function without defining it first.
The purpose of the function is to set a proper exit code when exiting from a script regardless of how the script was run. Normally you'd run PowerShell scripts like this:
powershell.exe -File "C:\your.ps1"
And in that case a simple exit $exitcode
would be sufficient:
C:\> type test1.ps1 exit 23 C:\> powershell -File .\test1.ps1 C:\> echo %errorlevel% 23
However, another way to execute PowerShell scripts is the -Command
parameter (since PowerShell scripts can be run directly from PowerShell). The difference between the -File
and -Command
parameters is that the latter returns only 1 or 0 (indicating whether or not the script exited with a non-zero exit code), but not the exit code itself.
C:\> powershell -Command .\test1.ps1 C:\> echo %errorlevel% 1
When omitting the parameter entirely PowerShell defaults to -Command
(allowing you to easily run PowerShell statements from the commandline):
C:\> powershell .\test1.ps1 C:\> echo %errorlevel% 1
Defining an exit code via $host.SetShouldExit()
ensures that the exit code is returned correctly when the script is invoked via powershell. -Command
. You still should exit with the actual exit code, though, because otherwise the exit code would only be set when running the script via powershell.exe -Command
, but not when running the script via powershell.exe -File
:
C:\> type test2.ps1 function ExitWithCode($exitcode) { $host.SetShouldExit($exitcode) exit } ExitWithCode 23 C:\> powershell -File .\test2.ps1 C:\> echo %errorlevel% 0 # ← exit without argument defaults to 0! C:\> powershell -Command .\test2.ps1 C:\> echo %errorlevel% 23
C:\> type test3.ps1 function ExitWithCode($exitcode) { $host.SetShouldExit($exitcode) exit $exitcode } ExitWithCode 23 C:\> powershell -File .\test3.ps1 C:\> echo %errorlevel% 23 C:\> powershell -Command .\test3.ps1 C:\> echo %errorlevel% 23
Upvotes: 32
Reputation: 2629
The way to return an exit code from PowerShell is to do exit $exitcode
. Internally, When the runtime processes the exit
keyword it will do the call to $host.SetShouldExit($exitcode)
for you. But be aware that exit
also exits scripts so it matters how you run your script. If want to run a script that calls exit
from powershell.exe
use the -File
parameter rather than the -Script
as in
powershell -File scriptThatCallsExit
Upvotes: 4
Reputation: 1999
$host.SetShouldExit($exitcode)
and exit
are two commands which have to separated. either with a return (like you mentioned) or with a semicolon:
function ExitWithCode { param($exitcode) $host.SetShouldExit($exitcode); exit }
Upvotes: 6