MacTheZazou
MacTheZazou

Reputation: 318

How to execute powershell script from python with space in the path and parameters

Path example: C:\Users\user\Some Space\dev\sensor\

def readSensorList():
    with open('sensor.json') as json_data:
        data = json.load(json_data)
    json_data.close()

return data
def executeSensor():
    sensorList = list()
    sensorListRaw = readSensorList()
    for sensor in sensorListRaw['sensors']:
        x = [subprocess.Popen(['powershell.exe','-ExecutionPolicy', 'Unrestricted', sensorListRaw["path"] + sensor["filename"], "-name", sensor["name"]], stdout=subprocess.PIPE, stderr=subprocess.PIPE), sensor["name"]]
        sensorList.append(x)
    return sensorList

Json containing the path:

{
                "path": "C:\\Users\\\\sensor\\",
                "sensors": 
                    [{
                        "name": "partition",
                        "filename": "partition.ps1"
                    }]
            }

In my test environnement there were no space in the path, but now, in the production, I have some space in the path, and I'm clueless about how to execute powershell script from python with space and parameters

I tried to quote the path, without any success.

Edit: I'm sure the problem come from the powershell.exe command and not from python, because:

powershell.exe -ExecutionPolicy 'Unrestricted C:\Users\some space\Desktop\PythonSensor\sensor\sensor.ps1'

Don't work in the CMD.

Upvotes: 4

Views: 6415

Answers (3)

T S
T S

Reputation: 1905

Although process parameters/arguments are indeed handled somewhat inconsistent on Windows, this is not the problem in this case. Pythons submodule.Popen handles this correctly (based on these rules). (Serge Ballestas example of calling .bat files does not really fit, because .bat files are executed by cmd.exe and cmd.exe is almost the only application these days that doesn't follow these rules.)

The problem in this case, is the usage of -Command:

When PowerShell is called with the -Command parameter, all following arguments are joined to a single command (like $args -join " "). This command is then executed.

-Command is the default parameter: If a Parameter does not start with - or /, powershell behaves, as if -Command were present before.

This:

subprocess.Popen(['powershell.exe', r'C:\Users\user\Some Space\dev\sensor\partition.ps1', "-name", "partition"])

is therefore equivalent to this command in an interactive powershell session:

C:\Users\user\Some Space\dev\sensor\partition.ps1 -name partition

If you surround each space with quotes (as described in your own answer), the resulting powershell command is:

C:\Users\user\Some' 'Space\dev\sensor\partition.ps1 -name partition

This is allowed and works - but if your path contains other special characters like $ or ` it will break again. It will also break if the script argument contains spaces or other special characters.

tl;dr:

To correctly call a powershell script from outside of powershell, use -File parameter:

subprocess.Popen(['powershell.exe','-ExecutionPolicy', 'Unrestricted', '-File', sensorListRaw["path"] + sensor["filename"], "-name:", sensor["name"]])

The parameter after -File is the script name (without any additional strange escaping), all following parameters are arguments to the script.

Notice the : appended to the parameter name (-name: instead of -name) - in most cases it will also work without the :, but if the corresponding value starts with a dash, it will fail without :.

Upvotes: 2

MacTheZazou
MacTheZazou

Reputation: 318

I finally found, you have to put ' between space like this:

powershell -command "C:\Users\me\Desktop\test\ps' 'test.ps1"

In python:

subprocess.Popen(['powershell','-ExecutionPolicy', 'Unrestricted', '-command', path]

with path as:

C:\Users\me\Desktop\some' 'space\sensor\ 

without " as Serge Ballesta explain.

Upvotes: 5

Serge Ballesta
Serge Ballesta

Reputation: 149185

The problem is not only with Python but also with Windows. Internally Unix-like system use an execve system call that takes as parameters the (path to) executable file and the arguments that will be passed to the command.

In Windows the system API has the function CreateProcess that takes as arguments the path to the executable file and the whole command line as a single string. It is then up to the child process to parse the command line to find its parameters

Python does its best to follow Windows internal usages to build a command line that will be parsed as expected, but it fails as soon as one parameter contains double quotes (").

For example, if foo.bat is a batch script taking 3 parameters, this will be correct:

 proc = subprocess.Popen(('foo.bat', 'a', 'b c', 'd'),
    stdout= subprocess.PIPE, stderr=subprocess.PIPE)

foo will receive 3 parameters, a, b c and d.

But if you use:

 proc = subprocess.Popen(('foo.bat', '"a"', '"b c"', '"d"'),
    stdout= subprocess.PIPE, stderr=subprocess.PIPE)

foo will receive 4 parameters: "a", "b, c" and "d".

TL/DR: you must ensure that the string that contains a parameter that contains a space is not enclosed in "

Upvotes: 3

Related Questions