Python Popen does not work in compound command (PowerShell)
I am trying to use Python Popen to change my working directory and execute a command.
pg = subprocess.Popen("cd c:/mydirectory ; ./runExecutable.exe --help", stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
buff,buffErr = pg.communicate()
However, powershell returns "The system cannot find the path specified." There is a way...
If I run
pg = subprocess.Popen("cd c:/mydirectory ;", stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
it returns the same.
However, if I run this: (no semicolon)
pg = subprocess.Popen("cd c:/mydirectory",stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
The command returns without errors. This leads me to believe that the semicolon is the problem. What is the reason for this behavior and how can I get around it?
I know I can just do c: /mydirectory/runExecutable.exe --help, but I would like to know why this is happening.
UPDATE:
I have tested passing the path to powershell as an argument for the Popen parameter executable
. It just powershell.exe
might not be enough. To find the true absolute path powershell
, execute where.exe powershell
. Then you can transfer it to Popen. Please note that shell
is still true. It will use the default shell, but pass the commandpowershell.exe
powershell = C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
pg = subprocess.Popen("cd c:/mydirectory ; ./runExecutable.exe", stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True, executable=powershell)
buff,buffErr = pg.communicate()
//It works!
source to share
In your call subprocess.Popen()
shell=True
means to use the platform default wrapper.
While the Windows world is - commendable - the move from CMD ( cmd.exe
) to PowerShell, Python determines which shell to invoke based on an environment variableCOMSPEC
that still points tocmd.exe
, even in the latest W10 update, which moved to PowerShell in terms of what the graphical user offers as the default shell.
For backward compatibility, this will not change anytime soon, and may never change.
Therefore your options are :
-
Use the syntax
cmd
as mentioned in the answer by Maurice Meyer . -
Don't use
shell = True
and explicitly callpowershell.exe
- see below. -
Windows only: override environment variable
COMSPEC
before useshell = True
- see below.
A simple Python example to invoke a binary file directly powershell
using command line commands, followed by a single line containing the PowerShell source code to execute:
import subprocess
args = 'powershell', '-noprofile', '-command', 'set-location /; $pwd'
subprocess.Popen(args)
Note that I deliberately used powershell
instead powershell.exe
because it opens up the possibility of a command running on Unix platforms after PowerShell Core .
Windows only: example c shell = True
, after overriding the environment variable, COMSPEC
first tell PowerShell:
import os, subprocess
os.environ["COMSPEC"] = 'powershell'
subprocess.Popen('Set-Location /; $pwd', shell=True)
Note:
-
COMSPEC
handled only on Windows; on Unix platforms, the shell executable is unchanged/bin/sh
-
Starting with Windows PowerShell v5.1 / PowerShell Core v6-beta.3, calling
powershell
with only-c
(interpreted as-Command
) still loads the default profile files, which might have unexpected (with the explicit callpowershell
used above-noprofile
suppresses this).- Changing the default behavior to not load profiles is the subject of this GitHub issue to align the PowerShell CLI with that of POSIX-like shells.
source to share