Why does os.system recognise space in a path when a filename exists but need spaces escaped to give a correct error when a file doesn't exist?

Question:

On Windows, why does os.system recognise space in a path when a filename exists but not when a file doesn’t exist?

Note that I know it’s recommended to use subprocess, and I am somewhat familiar with subprocess.run and subprocess.Popen but i’m asking here about os.system

I understand that os.system calls something similar to CMD in that the error message is the same or largely the same.

I will use wordpad.exe as an example because it’s on Windows systems by default, so is easy to test.

wordpad.exe does exist. zordpad.exe doesn’t exist.

import os

os.system(R'"C:Program FilesWindows NTAccessorieswordpad.exe"')

os.system(R'"C:Program FilesWindows NTAccessorieszordpad.exe"')

I’m using R there to make it a raw string so that the backslash is fine for whatever path. (though I could remove the R and put there).

And i’m using single quotes around the double quotes, so as to make the single quotes literal, so that when cmd gets called, it will see the double quotes as special and then preserve the spaces.

The first line in that python code works, it opens wordpad.exe fine.

The second line in that python code, trying to open "zordpad.exe" (a file that doesn’t exist), gives an error

C:blah>python issuewithos_dot_system4.py
'C:Program' is not recognized as an internal or external command,
operable program or batch file.

C:blah>

So it breaks on the space, but only when the file doesn’t exist!!!

CMD doesn’t have that problem

C:blah>"C:Program FilesWindows NTAccessorieszordpad.exe"
'"C:Program FilesWindows NTAccessorieszordpad.exe"' is not recognized as an internal or external command, operable program or batch file.

C:blah>

I could escape the space with the cmd escape character.. The escape character by cmd is ^

And so the following line works

os.system(R"C:Program^ FilesWindows^ NTAccessorieszordpad.exe")

(and I rightly don’t use single quotes around the double quoted string above, I don’t want to have single quotes around the double quotes there, otherwise i’d be making the double quotes literal which goes to cmd(or "cmd") as special and on cmd would make the caret literal, and would be a funny file path).

So back to the question..

Why is it that this os.system(R'"C:Program FilesWindows NTAccessorieswordpad.exe"') works

(imply that that syntax ensures spaces are preserved)

But this os.system(R'"C:Program FilesWindows NTAccessorieszordpad.exe"') (trying to call an executable that doesn’t exist), gives not just an error message (an error message would be expected). But it gives a broken error message, and the broken error message it gives logically implies that spaces weren’t preserved. (so it thinks C:Program is the name of the executable, perhaps being passed an argument of "Files...")

How do you explain that behaviour with the broken error message when the file doesn’t exist? It’s not seeing the space as literal but OK so why did it manage to see the space as literal when the filename did exist?!

Added

I spoke to a python expert that thought maybe it used the CreateProcess WinAPI function though found that it used wsystem function https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/system-wsystem?view=msvc-170 https://github.com/python/cpython/blob/main/Modules/posixmodule.c#L4893 though the mystery of the broken error message remains.

Asked By: barlop

||

Answers:

That’s simply because os.system is effectively system() (as noted in the addendum to the question – specifically the wide char variant _wsystem, but it is effectively identical as per documentation), which runs commands as cmd /C command. Putting this exact setup into a cmd.exe:

C:UsersUser>cmd /C "C:Program FilesWindows NTAccessorieswordpad.exe"

C:UsersUser>cmd /C "C:Program FilesWindows NTAccessorieszordpad.exe"
'C:Program' is not recognized as an internal or external command,
operable program or batch file.

The message is derived as is from directly via system() calling cmd /C, and not a fault in Python. This can be proven by changing the relevant environment variable COMSPEC in a Python interactive shell to something that doesn’t expect the /c flag (e.g. the Python interpreter):

>>> import os, sys
>>> os.environ['COMSPEC']
'C:\Windows\system32\cmd.exe'
>>> os.environ['COMSPEC'] = sys.executable
>>> os.system('print')
C:UsersUserAppDataLocalProgramsPythonPython35-32python.exe: can't open
 file '/c': [Errno 2] No such file or directory
2
>>>

Note the attempt by Python to execute /c as it was the first argument passed to it through os.system, indicating that system() passes /c first before the rest of the command.

Now as to how to workaround this, this post on Super User suggested using double quotes, so take that advice and see (with COMSPEC restored; e.g. new Python interactive console):

>>> os.system(r'""C:Program FilesWindows NTAccessorieszordpad.exe""')
'"C:Program FilesWindows NTAccessorieszordpad.exe"' is not recognized as an
internal or external command,
operable program or batch file.
1
>>> os.system(r'""C:Program FilesWindows NTAccessorieswordpad.exe""')
0
>>>

Now the error message is reported correctly on the missing executable, while wordpad.exe still gets executed as expected.

Answered By: metatoaster
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.