Python - Windows - Popen(shlex.split(command), shell=False causes OSError: [Errno 2] No such file or directory

I am running this code which works fine in OSX but causes an error on Windows:

command = "C:\\progra~2\\itms\\iTMSTransporter -m verify -f /Volumes/Stuff/Temp/TMP_S_0_V_TV2.itmsp -u username -p password -o /Volumes/Stuff/Temp/TMP_S_0_V_TV2.itmsp/LOGFILE.txt -s provider -v eXtreme"
self.process1 = Popen(shlex.split(command), shell=False, stdin=PIPE)

The error I am recieving on Windows is:

WindowsError: [Error 2] The system cannot find the file specified

Why is it giving me this error on Windows?


Your shlex.split() destroys your path because of removing \ characters. Let's check:

import shlex
command = "C:\\progra~2\\itms\\iTMSTransporter -m verify -f  Volumes/Stuff/Temp/TMP_S_0_V_TV2.itmsp -u username -p password -o /Volumes/Stuff/Temp/TMP_S_0_V_TV2.itmsp/LOGFILE.txt -s provider -v eXtreme"
print shlex.split(command)

['C:progra~2itmsiTMSTransporter', '-m', 'verify', '-f', '/Volumes/Stuff/Temp/TMP_S_0_V_TV2.itmsp', '-u', 'username', '-p', 'password', '-o', '/Volumes/Stuff/Temp/TMP_S_0_V_TV2.itmsp/LOGFILE.txt', '-s', 'provider', '-v', 'eXtreme']

As you can see, path to executable is incorrect (C:progra~2itmsiTMSTransporter), so Popen can't find it.

Change your path separator to /, which is safe in both Linux/Windows environments:

command = "C:/progra~2/itms/iTMSTransporter -m verify -f  Volumes/Stuff/Temp/TMP_S_0_V_TV2.itmsp -u username -p password -o /Volumes/Stuff/Temp/TMP_S_0_V_TV2.itmsp/LOGFILE.txt -s provider -v eXtreme"
print shlex.split(command)

['C:/progra~2/itms/iTMSTransporter', '-m', 'verify', '-f', 'Volumes/Stuff/Temp/TMP_S_0_V_TV2.itmsp', '-u', 'username', '-p', 'password', '-o', '/Volumes/Stuff/Temp/TMP_S_0_V_TV2.itmsp/LOGFILE.txt', '-s', 'provider', '-v', 'eXtreme']

Popen() will handle this path correctly.


This might come a bit late, but maybe it is helpful for others having similar problems.

Before we start: I usually tend to use raw strings when handling windows paths to facilitate copy pasting as it accepts single backslash characters:

In [0]: "C:\\path\\to\\folder" == r"C:\path\to\folder"
Out[0]: True

As mentioned in Gabriel M's answer, the problem might come from shlex swallowing the backlashes. In this answer I want to provide a solution other than replacing the backslashes by slashes.

In [1]: import shlex

In [2]: command = r"C:\progra~2\itms\iTMSTransporter -m verify 
   ...: -f /Volumes/Stuff/Temp/TMP_S_0_V_TV2.itmsp -u username -p password
   ...: -o /Volumes/Stuff/Temp/TMP_S_0_V_TV2.itmsp/LOGFILE.txt -s provider -v eXtreme"

In [3]: shlex.split(command)[0]
Out[3]: 'C:progra~2itmsiTMSTransporter'

A simple solution to keep the backslashes is to pass the posix=False option to shlex.split():

In [4]: shlex.split(command, posix=False)[0]
Out[4]: 'C:\\progra~2\\itms\\iTMSTransporter'

As you stated in the other answer's comments, replacing slashes did not solve your problem, it might be that your actual problem lies in the paths that you pass to your script in -f and -o. Maybe the script that you pass those paths to expects backslash characters or wants to have the drive letter in the path. – In any case, it would be interesting to know if you have found a solution to this in the last 6 years, and what this solution was.


Further options

You can also use pathlib (an python3 standard library for OS aware path formatting) who's Path provide an as_posix() method to convert the path to a string with forward slashes. This will result in forward slashes in your shlex.split() output. This might be most useful if your path comes from a variable, and is not directly hard coded (in the latter case you might just change the (back)slashes):

In [5]: from pathlib import Path

In [6]: command = Path(r"C:\progra~2\itms\iTMSTransporter").as_posix() + " -m verify 
   ...: -f /Volumes/Stuff/Temp/TMP_S_0_V_TV2.itmsp -u username -p password
   ...: -o /Volumes/Stuff/Temp/TMP_S_0_V_TV2.itmsp/LOGFILE.txt -s provider -v eXtreme"

In [7]: shlex.split(command)[0]
Out[7]: 'C:/progra~2/itms/iTMSTransporter'

Alternatively, the following – an improved version of In [4] – is something that should work on either OS. In this case posix is determined via os.name. This also relies on the use of pathlib for OS aware path formatting.

In [8]: import os

In [9]: command = str(Path(r"C:\progra~2\itms\iTMSTransporter")) + " -m verify 
   ...: -f /Volumes/Stuff/Temp/TMP_S_0_V_TV2.itmsp -u username -p password
   ...: -o /Volumes/Stuff/Temp/TMP_S_0_V_TV2.itmsp/LOGFILE.txt -s provider -v eXtreme"

In [10]: shlex.split(command, posix=(os.name == "posix"))[0]
Out[10]: 'C:\\progra~2\\itms\\iTMSTransporter'