19

I have a python application that has a shell that needs to do some setup based on whether the shell it's running in is the Windows Command Prompt (cmd) or Powershell.

I haven't been able to figure out how to detect if the application is running in powershell or cmd.

From my searches on stackoverflow and Google, it seems the only way to do this is to use psutil to find the name of the parent process.

Is there a nicer way?

Neuron
  • 5,141
  • 5
  • 38
  • 59
user6371867
  • 201
  • 2
  • 5
  • I'm confused about your situation. cmd and Powershell are called in entirely different ways. I'm not sure why you would not know how your code is calling sub-process. – EBGreen May 23 '16 at 15:19
  • I have a console python application. One of the features it offers is a customized cmd or powershell shell (basically setting up some environment variables etc.) for users to run other applications. If the user starts the python application from cmd then I want the customized shell to be a customized cmd shell, and likewise for powershell. Based on whether it is cmd or powershell the customization is slightly different, so I need to detect if the application is running in cmd or powershell. – user6371867 May 23 '16 at 15:25
  • 1
    In that case, you can try a command that you know will only work in Powershell and check for an error. Otherwise it is really a Python issue not a powershell issue. You could query the Win32_Process class in python for your process. Then get the PID of the parent process and query for that process to see what it is. – EBGreen May 23 '16 at 15:32
  • Yep, I tried running Poweshell commands like Get-ChildItem and Get-Host from the application through a subprocess.call, but in both powershell and cmd I get a traceback with a "file not found". I think python tries to run an executable called "Get-ChildItem" or "Get-Host" but can't find them in PowerShell because they're inbuilt commands(?). Do you know which commands would work in powershell and not in cmd? Thanks for your help! – user6371867 May 23 '16 at 15:51
  • 1
    How about making the shell independent of launch shell? Add a menu that provides two shells so the user can decide whether cmd or psh is more suitable. – vonPryz May 23 '16 at 16:00
  • That's exactly how I have it now, but we've gotten some user feedback saying it would be nice to be able to automatically launch the right shell, especially since some users don't know what shell they're on currently. – user6371867 May 23 '16 at 16:08
  • 1
    I think `psutil` is your best option. Your Python script is run by the Python interpreter regardless of whether it was launched from a PowerShell or CMD process. – Ansgar Wiechers May 23 '16 at 16:30
  • If the user does not know enough to choose between `cmd` and `PowerShell`, what are they going to do when they get one open? – lit May 23 '16 at 17:11
  • They will run some commands. Its analogous to checking email - some people dont want to be bothered about whether they're running Firefox or Chrome, anything works so long as gmail pops up. – user6371867 May 23 '16 at 17:33
  • 1
    Python has the ability to query WMI so I still think that is a better route than psutil since it avoids a dependency. – EBGreen May 23 '16 at 20:07
  • 4
    Your script is not running *in* either cmd.exe or powershell.exe. If it's executed by python.exe, then it's *attached* to a console window that's implemented by conhost.exe and has nothing to do with either cmd.exe or powershell.exe. Python's `os.system` uses cmd.exe. The `subprocess` module uses WinAPI `CreateProcess`, which creates a process using a PE executable (i.e. .EXE, but the extension doesn't matter) or starts cmd.exe when passed the path to a .BAT or .CMD file. The `shell=True` option of subprocess uses the `%ComSpec%` shell, which is usually cmd.exe. – Eryk Sun May 23 '16 at 21:02
  • 4
    **All existing answers to this question are functionally useless.** Moreover, the OP failed to detail how they leveraged the third-party `psutil` package in their question edits. Instead, see [this genuinely useful answer](https://stackoverflow.com/a/55598796/2809027) to a [similar question elsewhere](https://stackoverflow.com/questions/55597797/detect-whether-current-shell-is-powershell-in-python). *tl;dr:* `is_powershell = bool(re.fullmatch('pwsh|pwsh.exe|powershell.exe', psutil.Process(os.getppid()).name()))`. – Cecil Curry Jun 11 '19 at 00:54

4 Answers4

4

@Matt A. is right. Use psutil and os package to get the parent process id and the name.

parent_pid = os.getppid()
print(psutil.Process(parent_pid).name())
Yash Gupta
  • 331
  • 2
  • 8
1

The following snippet finds md5sum on args.file in bash/powershell, I usually use the first command to check what we are running in, so I can use it later on, using shell=True in subprocess is not very portable.

import os, subprocess
running_shell = None
mycheck='/usr/bin/md5sum'   # full path is needed
if not os.path.isfile(mycheck):
    try:
        file_md5sum = subprocess.check_output("powershell.exe Get-FileHash -Algorithm MD5 {} | Select -expand Hash".format(args.file).split())
    except FileNotFoundError as e:
        log.fatal("unable to generate md5sum")
        sys.exit(-1)
    file_md5sum = file_md5sum.lower()
    running_shell = 'powershell'
else:
    file_md5sum = subprocess.check_output([mycheck, args.file])
    running_shell = 'bash'
MortenB
  • 2,749
  • 1
  • 31
  • 35
0

Here is a sample powershell stub that can do the trick:

$SCR="block_ips.py"
$proc = $null
$procs = Get-WmiObject Win32_Process -Filter "name = 'python3.exe' or name = 'python.exe'" | Select-Object Description,ProcessId,CommandLine,CreationDate

$procs | ForEach-Object { 
    if ( $_.CommandLine.IndexOf($SCR) -ne -1 ) { 
        if ( $null -eq $proc ) {
            $proc = $_
        }
    }
}

if ( $null -ne $proc ) {
    Write-Host "Process already running: $proc"
} else {
    Write-Host "$SCR is not running"
}
Timothy C. Quinn
  • 3,739
  • 1
  • 35
  • 47
-1

Based on this post, you should be able to run this CMD/PS command through the subprocess module in your Python script:

subprocess.call("(dir 2>&1 *`|echo CMD);&<# rem #>echo PowerShell", shell=True)

This will output CMD if you're in CMD, and PowerShell if you're in PS.

nxl4
  • 714
  • 2
  • 8
  • 17
  • 3
    `call` and `Popen` apparently invoke cmd.exe for execution, rather than the parent shell. Thus even if python is started from within PowerShell, this command will return `CMD` as the answer. – sfuqua Oct 16 '18 at 15:48
  • I wonder if there is a way to set what kind of shell is invoked by Python? – not2qubit Apr 27 '20 at 22:13