Sydney
Sydney

Reputation: 43

Why won't the "Exit" function work in my PowerShell code?

When I try the "Exit" function. It does not close the console. It doesn't do anything. How can I change it to make it exit the console?

Write-Host "-------------- MENU ---------------"
Write-Host "Utility Menu"
Write-Host "1 = Help Ipconfig"
Write-Host "2 = Ipconfig"
Write-Host "3 = Notepad"
Write-Host "4 = Calculator"
Write-Host "5 = Exit"
Write-Host "-----------------------------------"

$userinput = Read-Host "Please select a menu item: "

switch ( $userinput )
{
  1 {Help Ipconfig}
  2 {ipconfig}
  3 {start notepad}
  4 {start calc.exe}
  5 {Exit}
}

Upvotes: 4

Views: 8262

Answers (4)

mklement0
mklement0

Reputation: 437042

There is helpful information in the existing answers, but I feel that conceptual framing is called for:

exit [...] It does not close the console.

It is unusual for a script to want to terminate the entire interactive PowerShell session (which is what would lead to the console window closing), and exit would only do so outside a script - see the bottom section for details.

If exiting the session as a whole from a script or function[1] is really your intent, use [Environment]::Exit(0), as suggested by Colyn1337. (0 is the exit code to report on process termination, which by convention signals success; when closing an interactive session, this exit code is likely irrelevant.)


Background information

Language keywords for exiting various scopes from PowerShell:

  • break is used to exit just a switch statement or a loop statement (foreach / for, while, do); that is, execution continues in the same scope after such a statement - see the conceptual about_Break help topic.

    • Pitfall: If you use break or continue without an enclosing loop or switch statement, PowerShell looks up the call stack for such a statement and exits the first such statement it finds; if there is none, the current call stack is terminated; that is, at the very least the enclosing script terminates as a whole.
      • Note that a ForEach-Object call (whose built-in aliases are % and, somewhat confusingly, foreach), invariably in a pipeline, is not a loop; to "break" out of a script block passed to ForEach-Object, i.e. to move on to the next pipeline input object, use return.
  • return is used to exit an enclosing script file (if executed directly in the script file's top-level scope) or function or script block ({ ... }), optionally by first outputting data via an expression or command passed as an argument (e.g. return 'foo'); see the conceptual about_Return help topic.

    • return has no effect on the exit code reported by a script and therefore potentially by a PowerShell process as a whole; only exit can be used to set the exit code - see below.
  • exit is used to exit an interactive session or the enclosing script file and to optionally set an exit code:

    • Outside of a script file, exit exits an interactive PowerShell session as a whole (closes the console window).

    • Inside of a script file, exit always exits that script (only) - even when called from a function inside that script.

      • Note the contrast with return, which from a function just exits that function, whether called from inside a script or not.
    • exit $exitCode - where $exitCode stands for any integer[2] - can be used to set a script / session's exit code; using just exit is the same as exit 0:

      • Since PowerShell's error handling isn't based on exit codes (unlike that of shells such as cmd.exe and bash), setting an exit code is primarily of interest for communicating success vs. failure to outside callers.

      • If you execute a script from the outside via PowerShell's CLI (e.g., pwsh -File script.ps1), the exit code set via exit $exitCode will also become the process exit code, which the outside caller - e.g., a CI tool - can then inspect.

        • Session-internally, an exit code set by a script via exit $exitCode is reflected in the automatic $LASTEXITCODE variable, though note that this variable is also set by any call to an external program, to that program's process exit code.
      • See this post for more information about the relevance and use of exit codes in PowerShell.


[1] As postanote's answer shows, exit from inside a function that has been dot-sourced (loaded directly into) the global scope, exits the whole session too. However, it is better to explicitly signal the unusual intent to exit the entire session via [Environment]::Exit(0).

[2] Technically, you can pass any expression or command to exit (as with return, though the expression/command's output becomes data there, as-is), but anything that cannot at least be converted to an integer - e.g., exit 'not a number' - is quietly ignored and is the same as exit 0. Also note that the range of valid integers varies by platform: Windows supports [int] values, i.e. signed 32-bit values that include negative numbers. By contrast, Unix-like platforms only support unsigned [byte] values, i.e. numbers from 0 to 255 (negative numbers are converted to positive ones, and only the lowest byte is considered for larger values ).

Upvotes: 7

Colyn1337
Colyn1337

Reputation: 1713

The Exit command is really like a function within the powershell parser. There's some packaging around it and when it executes, Exit doesn't always mean Exit. It quite often will be replaced by the parser with a Return.

To get around that, you can instruct the runtime to terminate with the following code:

[System.Environment]::Exit(0)

The integer in the Exit method is the return code you want the runtime to out as it exits. Typically anything other than 0 will be interpreted as an error.

Upvotes: 5

postanote
postanote

Reputation: 16076

Continuing from my comment...

Gif of your code and menu selection

Execution

Running your code a straight script will do nothing on the exit option. Refactoring your script as a function does work.

# Saved as Start-Menu.ps1
Function Start-Menu {
    Clear-Host

    Write-Host "-------------- MENU ---------------"
    Write-Host "Utility Menu"
    Write-Host "1 = Help Ipconfig"
    Write-Host "2 = Ipconfig"
    Write-Host "3 = Notepad"
    Write-Host "4 = Calculator"
    Write-Host "5 = Exit"
    Write-Host "-----------------------------------"

    $userinput = Read-Host "Please select a menu item: "

    switch ( $userinput )
    {
      1 {Help Ipconfig}
      2 {ipconfig}
      3 {start notepad}
      4 {start calc.exe}
      5 {Exit}
    }
}

Another gif on the action.

Using a function

Or use what Colyn1337, gave you or this...

    switch ( $userinput )
    {
      1 {Help Ipconfig}
      2 {ipconfig}
      3 {start notepad}
      4 {start calc.exe}
      5 {Stop-Process -Id $PID}
    }

...(which gets the current process ID of the PowerShell session) and thus no need for the function and just run the script as-is, and note though the namespace is slower to close than the function or $PID approach.

Using .Net Namespace

Upvotes: 4

anymous
anymous

Reputation: 52

Use break instead of exit.

Write-Host "-------------- MENU ---------------"
Write-Host "Utility Menu"
Write-Host "1 = Help Ipconfig"
Write-Host "2 = Ipconfig"
Write-Host "3 = Notepad"
Write-Host "4 = Calculator"
Write-Host "5 = Exit"
Write-Host "-----------------------------------"

$userinput = Read-Host "Please select a menu item: "

switch ( $userinput )
{
  1 {Help Ipconfig}
  2 {ipconfig}
  3 {start notepad}
  4 {start calc.exe}
  5 {break}
}

Upvotes: 0

Related Questions