9

What's the recommended way to check which shell is my python program running inside? I know I can query os.environ.get('SHELL') for the shell name, but what can I use to determine powershell?

Nehal J Wani
  • 16,071
  • 3
  • 64
  • 89

2 Answers2

17

Generally, environment variable SHELL does not tell you what shell invoked your script, it only tells you the binary path of the current user's default shell (on Unix-like platforms), as chepner notes.

To detect what shell invoked your script, you must examine the script's parent process.

The following works with the psutil package installed (it is both v2- and v3-compatible):

import os, psutil, re

# Get the parent process name.
pproc_name = psutil.Process(os.getppid()).name()

# See if it is Windows PowerShell (powershell.exe) or PowerShell Core (pwsh[.exe]):
is_power_shell = bool(re.fullmatch('pwsh|pwsh.exe|powershell.exe', pproc_name))

If installing a package is not an option, you can use the following workaround to detect PowerShell specifically, but note the constraints:

  • It presumes a standard PowerShell installation, specifically with respect to environment variable PSModulePath: that is, PSModulePath must either:

    • not be predefined at all outside of PowerShell (Unix-like platforms)
    • or must have just one or two entries outside of PowerShell (Windows, predefined via the registry)[1].
  • It presumes that the script wasn't invoked via nested shell invocations:

    • If you launched a different shell from PowerShell, which then launched your script, the solution below would still indicate that your script was launched by PowerShell.

    • Conversely, if you launched wsl.exe or WSL's bash.exe or MSYS' bash.exe / sh.exe from PowerShell, which then launched your script, the solution below would not indicate that the script was (indirectly) launched by PowerShell, because these executables do not inherit the caller's environment variables; by contrast, the bash.exe / sh.exe that comes with Git and the ones that comes with Cygwin do.

import os

is_power_shell = len(os.getenv('PSModulePath', '').split(os.pathsep)) >= 3 

Note that when PowerShell starts up it ensures that at least 3 locations are present in PSModulePath, on all platforms (the in-box/system-modules location, the all-users location, and the current-user location). As stated, outside of PowerShell the variable isn't predefined at all on Unix, and on Windows it is predefined with at most 2 locations.


[1] Older Windows 10 versions predefined just the system-modules location, $env:SystemRoot\System32\WindowsPowerShell\v1.0\Modules, whereas more recent versions additionally predefine the all-users location, $env:ProgramFiles\WindowsPowerShell\Modules.

Benjamin Loison
  • 3,782
  • 4
  • 16
  • 33
mklement0
  • 382,024
  • 64
  • 607
  • 775
  • Great answer! Is there a way to extend this to determine if you're in a `Powershell` ("Desktop") or `PWSH` ("Core")? – not2qubit Apr 27 '20 at 21:46
  • 1
    @not2qubit In the first solution, examine `pprocName`; in the second solution, you can test the value of `os.getenv('PSModulePath', '')` against regex `\bPowerShell\b` - if it matches, the parent process is PowerShell _Core_ (`pwsh`). – mklement0 Apr 27 '20 at 21:59
  • 1
    Great! I'm trying to avoid having to install *psutil* as I'm making a test script checking correct (python) installation. So I'm looking to do some process + powershell magic to figure this out. So I'll see if I can add that to my [other post](https://stackoverflow.com/a/61469226/1147688). – not2qubit Apr 27 '20 at 22:11
1

If you do not want to depend on external packages, the suggested PSModulePath check does not work reliably on windows, it gives false positives for cygwin, git bash and in WSL.

However, common for both a powershell window and a cmd window is that they both have the environmental variable PROMPT set to $P$G (default value from 80s DOS). Thus the following works

import os
isCmdOrPowershell = (os.getenv('PROMPT', '') == '$P$G')

for instance as a reliable test of if the terminal supports ANSI colours (cmd and powershell windows: no. cygwin, git bash and WSL windows: yes).

Benjamin Loison
  • 3,782
  • 4
  • 16
  • 33
hlovdal
  • 26,565
  • 10
  • 94
  • 165
  • PowerShell doesn't define a `PROMPT` environment variable, only `cmd.exe` does, so this won't tell if PowerShell _alone_ launched your Python script (only if you start PowerShell _from `cmd.exe`_ will you see such an env. var. in PowerShell, because the PowerShell process then _inherits it_). – mklement0 Mar 14 '21 at 20:21
  • Also, there are probably better ways of detecting support for ANSI (Virtual Terminal) escape sequences. Also note that Windows console windows do support them _on demand_ (see [this answer](https://stackoverflow.com/a/51681675/45375). If distinguishing along the lines of Windows vs. Unix emulation/subsystem is sufficient, you can use `os.name != 'nt'` or inspect the `TERM` environment variable (it isn't normally defined in Windows, but note that, at least hypothetically, the value in Unix-like environments can also indicate _non_-support of ANSI escape sequences via a value of `dumb`). – mklement0 Mar 14 '21 at 20:44