1

This question has been asked before, but PowerShell has since gone through several phases that it even has a different executable name now (pwsh.exe). All answers assume powershell.exe will never change.

Is there cross-platform a way on .NET to get path to the latest (or preferably, user-preferred) PowerShell executable without resorting to "try to find pwsh.exe in the PATH first, if it doesn't work, try finding powershell.exe" trickery? Obviously, that approach can fail someday too if pwsh.exe changes its name again. Not to mention that a user's preferences might be different than what's installed on the system.

Even MS-DOS had this feature with COMSPEC environment variable, let alone Unix and its SHELL variable. I'm surprised that PowerShell doesn't have this.

Perhaps, it's possible to get that information from PowerShell's runtime variables, but I'll be calling it from an external .NET process.

Sedat Kapanoglu
  • 46,641
  • 25
  • 114
  • 148
  • 1
    What's the end goal? you can get the powershell path using your assembly with `Path.ChangeExtension(Environment.GetCommandLineArgs()[0], "exe");` – Santiago Squarzon Jul 11 '23 at 21:49
  • 2
    You don't even need to run `pwsh` if you want to execute Powershell from C#, you can just do `Powershell.Create` https://learn.microsoft.com/en-us/powershell/scripting/developer/hosting/adding-and-invoking-commands?view=powershell-7.3 – Charlieface Jul 11 '23 at 21:57
  • 3
    Did you read the answers to the post you linked? Windows PowerShell (2 through 5) always have the same default install location, as it ships with the operating system. PowerShell (6 through 7) is a standalone application you can install multiple versions of, side-by-side. – Mathias R. Jessen Jul 11 '23 at 21:57
  • After running your MS-DOS commands on UNIX. Try getting the information for PowerShell from the UNIX registry. Good luck. – Tu deschizi eu inchid Jul 11 '23 at 22:14
  • @MathiasR.Jessen Did you read my question? I want to run the latest or what user prefers (if there's such a thing) regardless of if it's OS included or external. – Sedat Kapanoglu Jul 11 '23 at 22:17
  • @SantiagoSquarzon what you recommended would just generate the path to my own executable not PowerShell's. The end goal is to run the correct (user-preferred or latest) PowerShell instance from an external process. As I understand, it's impossible to know currently. – Sedat Kapanoglu Jul 11 '23 at 22:32
  • Identifying the user's preferences regardless of what is installed on the machine will probaby require AI resources. – lit Jul 11 '23 at 23:41
  • @lit Yes, if I'd said "regardless of if it's installed or not." – Sedat Kapanoglu Jul 11 '23 at 23:44
  • @SedatKapanoglu, is there anything to indicate a user's preference other than presence in `$Env:Path`? You could use the first `pwsh.exe` or `powershell.exe` found in a directory from `$Env:Path -split [IO.Path]::PathSeparator`. Of course, if `powershell.exe` is used, the code must be written and maintained for the older `Windows PowerShell` and not make use of any `PowerShell Core` capabilities. – lit Jul 12 '23 at 13:31

1 Answers1

4
  • There is no correct PowerShell executable:

    • Window PowerShell is the legacy, ships-with-Windows, Windows-only PowerShell edition whose latest and last version is 5.1 and which will receive critical security fixes only.

      • The location of its executable, powershell.exe, is the same for all its versions[1] (expressed in C# terms):

        System.Environment.ExpandEnvironmentVariables(
          @"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe"
        );
        
      • Note: If your application is a 32-bit application and you want to target the 64-bit PowerShell executable, replace System32 with SysNative; conversely, in order to target the 32-bit PowerShell executable from a 64-bit application, replace System32 with SysWOW64

    • PowerShell (Core) 7+ is its modern, cross-platform, install-on-demand successor, which is actively maintained.

      • This edition's executable name differs from that of Window PowerShell: it is pwsh.exe (on Windows) / pwsh (on Unix-like platforms).

        • As this edition evolves, it is highly unlikely that its executable name will change, given the continued commitment to backward compatibility (within reason).
      • Unlike Windows PowerShell, PowerShell (Core) allows multiple versions to be installed side-by-side, in potentially arbitrary locations.

    • The system-defined ComSpec environment variable contains the full path to what is still the default shell on Windows, cmd.exe (the legacy Command Prompt), and that is unlikely to change for reasons of backward compatibility.

      • No analogous environment variable was ever defined for PowerShell, given that it isn't the default shell.

      • As an aside: Unix-like platforms rely on "convention over configuration" and assume that the default shell is in a fixed location, namely /bin/sh


Given the above, your best bet on Windows[2] is indeed to:

  • Look for pwsh.exe via the Path environment variable: that is, consider the pwsh.exe executable, if any, found in the first directory listed in the Path environment variable to be the current user's preferred version.

    • See the answers to this post for how to locate an executable via Path in C#.

    • As Tu deschizi eu inchid points out, a potential alternative is to consult the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShellCore\InstalledVersions registry key for installed PowerShell (Core) versions (this answer shows VB.NET code that examines this key); however, note that:

      • This key is only created if PowerShell (Core) is installed via the MSI installer (which includes installation via the Microsoft Store and winget.exe), which, however, is just one way to install it (see an overview of all available installation methods here), so this may cause you to miss installations.

      • Conversely, a version installed this way isn't necessarily discoverable via the Path environment variable, and if it isn't, may signal the intent that it is a "private" installation.

  • And, if not found, fall back to powershell.exe's full path, which can be determined as shown above.

As a general caveat, given that you're willing to target whatever PowerShell version is available:

  • If you're willing to target Windows PowerShell as a fallback, any PowerShell code you use will have to be cross-edition compatible.
  • Even in the realm of PowerShell (Core) alone you'll need to be aware of features that are only available in later versions.

[1] Note that only ever one version of Windows PowerShell can be installed at a time, which in recent Windows version is the last one, v5.1. However, since v3, also running v2 (only) has been possible via a command-line parameter of the one and only powershell.exe executable (-version 2), assuming that v2 of .NET Framework is installed (which is no longer true by default in recent Windows versions).

[2] on Unix-like platforms, by definition there can only ever be (potentially multiple versions of) PowerShell (Core).

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • Thanks! This explains it clearly, but it's disappointing that there's no provision for a user-preferred instance of PowerShell. I might prefer pwsh over legacy because of many reasons (configuration, new features, performance, etc). – Sedat Kapanoglu Jul 11 '23 at 23:36
  • Glad to hear it, @SedatKapanoglu. Pragmatically speaking, you can think of the presence of the (first) `pwsh.exe` among the entries in the `Path` environment variable as _implicitly_ expressing a preference. Note that if you're willing to target Windows PowerShell as a fallback, any PowerShell code you use will have to be cross-edition compatible, and even in the realm of PowerShell (Core) alone you'll need to be aware of version-specific features. – mklement0 Jul 12 '23 at 01:15
  • Good post. However, for Windows, one may consider getting the version(s)/path(s) for PowerShell from the registry. See subkeys under `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell` and `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShellCore\InstalledVersions`. There's some code in this [post](https://stackoverflow.com/a/74005878/10024425) that shows how one can retrieve the information from the Windows registry. – Tu deschizi eu inchid Jul 12 '23 at 02:00
  • 1
    Thanks, @Tudeschizieuinchid - please see my update (bottom section), which now links to your answer, but also discusses the tradeoffs of this approach. (For _Windows PowerShell_ there's no good reason to consult the registry, given that the path is fixed). – mklement0 Jul 12 '23 at 12:43