mixed slashes with os.path.join on windows
Question:
I tend to use only forward slashes for paths (‘/’) and python is happy with it also on windows.
In the description of os.path.join it says that is the correct way if you want to go cross-platform. But when I use it I get mixed slashes:
import os
a = 'c:/'
b = 'myFirstDirectory/'
c = 'mySecondDirectory'
d = 'myThirdDirectory'
e = 'myExecutable.exe'
print os.path.join(a, b, c, d, e)
# Result:
c:/myFirstDirectory/mySecondDirectorymyThirdDirectorymyExecutable.exe
Is this correct? Should I check and correct it afterward or there is a better way?
Thanks
EDIT:
I also get mixed slashes when asking for paths
import sys
for item in sys.path:
print item
# Result:
C:Program FilesAutodeskMaya2013.5bin
C:Program FilesAutodeskMaya2013.5mentalrayscriptsAETemplates
C:Program FilesAutodeskMaya2013.5Python
C:Program FilesAutodeskMaya2013.5Pythonlibsite-packages
C:Program FilesAutodeskMaya2013.5binpython26.ziplib-tk
C:/Users/nookie/Documents/maya/2013.5-x64/prefs/scripts
C:/Users/nookie/Documents/maya/2013.5-x64/scripts
C:/Users/nookie/Documents/maya/scripts
C:Program FilesNuke7.0v4libsite-packages
C:Program FilesNuke7.0v4/plugins/modules
Answers:
os
adds slashes for you and makes sure not to duplicate slashes so omit them in your strings
import os
# Don't add your own slashes
a = 'C:'
b = 'myFirstDirectory'
c = 'mySecondDirectory'
d = 'myThirdDirectory'
e = 'myExecutable.exe'
print os.path.join(a, b, c, d, e)
C:myFirstDirectorymySecondDirectorymyThirdDirectorymyExecutable.exe
Additional:
I’m unsure as to why you have mixed slashes in your sys path (have you used a linux os to add some folders?) but try checking
print os.path.isdir(os.path.join('C:','Users','nookie'))
.
If this is True
then os
works for your mixed slashes.
Either way, I would avoid hard-coding directory names into your program. Your sys.path
for loop is a safe way to pull out these directories. You can then use some string methods, or regex to pick the desired folder.
You are now providing some of the slashes yourself and letting os.path.join
pick others. It’s better to let python pick all of them or provide them all yourself. Python uses backslashes for the latter part of the path, because backslashes are the default on Windows.
import os
a = 'c:' # removed slash
b = 'myFirstDirectory' # removed slash
c = 'mySecondDirectory'
d = 'myThirdDirectory'
e = 'myExecutable.exe'
print os.path.join(a + os.sep, b, c, d, e)
I haven’t tested this, but I hope this helps. It’s more common to have a base path and only having to join one other element, mostly files.
By the way; you can use os.sep
for those moments you want to have the best separator for the operating system python is running on.
Edit: as dash-tom-bang states, apparently for Windows you do need to include a separator for the root of the path. Otherwise you create a relative path instead of an absolute one.
You can use .replace()
after path.join()
to ensure the slashes are correct:
# .replace() all backslashes with forwardslashes
print os.path.join(a, b, c, d, e).replace("\","/")
This gives the output:
c:/myFirstDirectory/mySecondDirectory/myThirdDirectory/myExecutable.exe
As @sharpcloud suggested, it would be better to remove the slashes from your input strings, however this is an alternative.
EDIT based on comment: path = os.path.normpath(path)
My previous answer lacks the capability of handling escape characters and thus should not be used:
- First, convert the path to an array of folders and file name.
-
Second, glue them back together using the correct symbol.
import os
path = 'c:wwwappmy/folder/file.php'
# split the path to parts by either slash symbol:
path = re.compile(r"[/]").split(path)
# join the path using the correct slash symbol:
path = os.path.join(*path)
try using abspath (using python 3)
import os
a = 'c:/'
b = 'myFirstDirectory/'
c = 'mySecondDirectory'
d = 'myThirdDirectory'
e = 'myExecutable.exe'
print(os.path.abspath(os.path.join(a, b, c, d, e)))
OUTPUT:
c:myFirstDirectorymySecondDirectorymyThirdDirectorymyExecutable.exe
Process finished with exit code 0
If for any reason you need to provide the paths yourself and you have using anything above python 3.4 you can use pathlib
from pathlib import Path, PurePosixPath
a = PurePosixPath('c:/')
b = PurePosixPath('myFirstDirectory/')
c = 'mySecondDirectory'
d = 'myThirdDirectory'
e = 'myExecutable.exe'
print(a / b / c / d / e)
# Result
c:/myFirstDirectory/mySecondDirectory/myThirdDirectory/myExecutable.exe
I used this when I needed a user to provide the location of an assets directory and my code was looking up using windows path strings
In [1]: from pathlib import Path, PureWindowsPath
In [2]: USER_ASSETS_DIR = Path('/asset/dir') # user provides this form environment variable
In [3]: SPECIFIC_ASSET = PureWindowsPath('some\asset')
In [4]: USER_ASSETS_DIR / SPECIFIC_ASSET
Out[4]: PosixPath('/asset/dir/some/asset')
You can also do this:
import re
a = 'c:/'
b = 'myFirstDirectory/'
c = 'mySecondDirectory'
d = 'myThirdDirectory'
e = 'myExecutable.exe'
joined = os.path.join(a, b, c, d, e)
formatted = re.sub(r'/|\', re.escape(os.sep), joined)
This is going to switch all your potentially mixed slashes into OS compliant ones.
I know it’s an ancient topic but I couldn’t resist. 🙂
Postgres command client psql
doesn’t accept back slashes even on Windows:
>psql -U user -h 111.111.111.111 -d mydb
psql (12.2, server 12.5 . . .
. . .
mydb=> i C:mypathmyscript.sql
C:: Permission denied
So needed to fix it when executing from Python 3.8.6
. Didn’t want to resort to naive string replacement and used existing function:
script_path = Path(script_dir).resolve()
input_sql = f'\i {script_path.joinpath("myscript.sql").as_posix()}n'
But under the hood it has:
# ...ProgramsPythonPython38Libpathlib.py
def as_posix(self):
"""Return the string representation of the path with forward (/)
slashes."""
f = self._flavour
return str(self).replace(f.sep, '/')
The way I do it is fairly straightforward: rstrip
all the paths from their slashes, regardless of quantity and correctness, add join those paths back using the correct separator.
import os
def join_path_regardless_of_separators(*paths):
return os.path.sep.join(path.rstrip(r"/") for path in paths)
a = 'c:/'
b = 'myFirstDirectory/'
c = 'mySecondDirectory'
d = 'myThirdDirectory\\\/'
e = 'myExecutable.exe'
join_path_regardless_of_separators(a, b, c, d, e)
>>> 'c:\myFirstDirectory\mySecondDirectory\myThirdDirectory\myExecutable.exe'
Another way to use it, for the same result:
join_path_regardless_of_separators(*"""c:////\\
myFirstDirectory/
mySecondDirectory\\
myThirdDirectory/////
myExecutable.exe
""".split())
I tend to use only forward slashes for paths (‘/’) and python is happy with it also on windows.
In the description of os.path.join it says that is the correct way if you want to go cross-platform. But when I use it I get mixed slashes:
import os
a = 'c:/'
b = 'myFirstDirectory/'
c = 'mySecondDirectory'
d = 'myThirdDirectory'
e = 'myExecutable.exe'
print os.path.join(a, b, c, d, e)
# Result:
c:/myFirstDirectory/mySecondDirectorymyThirdDirectorymyExecutable.exe
Is this correct? Should I check and correct it afterward or there is a better way?
Thanks
EDIT:
I also get mixed slashes when asking for paths
import sys
for item in sys.path:
print item
# Result:
C:Program FilesAutodeskMaya2013.5bin
C:Program FilesAutodeskMaya2013.5mentalrayscriptsAETemplates
C:Program FilesAutodeskMaya2013.5Python
C:Program FilesAutodeskMaya2013.5Pythonlibsite-packages
C:Program FilesAutodeskMaya2013.5binpython26.ziplib-tk
C:/Users/nookie/Documents/maya/2013.5-x64/prefs/scripts
C:/Users/nookie/Documents/maya/2013.5-x64/scripts
C:/Users/nookie/Documents/maya/scripts
C:Program FilesNuke7.0v4libsite-packages
C:Program FilesNuke7.0v4/plugins/modules
os
adds slashes for you and makes sure not to duplicate slashes so omit them in your strings
import os
# Don't add your own slashes
a = 'C:'
b = 'myFirstDirectory'
c = 'mySecondDirectory'
d = 'myThirdDirectory'
e = 'myExecutable.exe'
print os.path.join(a, b, c, d, e)
C:myFirstDirectorymySecondDirectorymyThirdDirectorymyExecutable.exe
Additional:
I’m unsure as to why you have mixed slashes in your sys path (have you used a linux os to add some folders?) but try checking
print os.path.isdir(os.path.join('C:','Users','nookie'))
.
If this is True
then os
works for your mixed slashes.
Either way, I would avoid hard-coding directory names into your program. Your sys.path
for loop is a safe way to pull out these directories. You can then use some string methods, or regex to pick the desired folder.
You are now providing some of the slashes yourself and letting os.path.join
pick others. It’s better to let python pick all of them or provide them all yourself. Python uses backslashes for the latter part of the path, because backslashes are the default on Windows.
import os
a = 'c:' # removed slash
b = 'myFirstDirectory' # removed slash
c = 'mySecondDirectory'
d = 'myThirdDirectory'
e = 'myExecutable.exe'
print os.path.join(a + os.sep, b, c, d, e)
I haven’t tested this, but I hope this helps. It’s more common to have a base path and only having to join one other element, mostly files.
By the way; you can use os.sep
for those moments you want to have the best separator for the operating system python is running on.
Edit: as dash-tom-bang states, apparently for Windows you do need to include a separator for the root of the path. Otherwise you create a relative path instead of an absolute one.
You can use .replace()
after path.join()
to ensure the slashes are correct:
# .replace() all backslashes with forwardslashes
print os.path.join(a, b, c, d, e).replace("\","/")
This gives the output:
c:/myFirstDirectory/mySecondDirectory/myThirdDirectory/myExecutable.exe
As @sharpcloud suggested, it would be better to remove the slashes from your input strings, however this is an alternative.
EDIT based on comment: path = os.path.normpath(path)
My previous answer lacks the capability of handling escape characters and thus should not be used:
- First, convert the path to an array of folders and file name.
-
Second, glue them back together using the correct symbol.
import os path = 'c:wwwappmy/folder/file.php' # split the path to parts by either slash symbol: path = re.compile(r"[/]").split(path) # join the path using the correct slash symbol: path = os.path.join(*path)
try using abspath (using python 3)
import os
a = 'c:/'
b = 'myFirstDirectory/'
c = 'mySecondDirectory'
d = 'myThirdDirectory'
e = 'myExecutable.exe'
print(os.path.abspath(os.path.join(a, b, c, d, e)))
OUTPUT:
c:myFirstDirectorymySecondDirectorymyThirdDirectorymyExecutable.exe
Process finished with exit code 0
If for any reason you need to provide the paths yourself and you have using anything above python 3.4 you can use pathlib
from pathlib import Path, PurePosixPath
a = PurePosixPath('c:/')
b = PurePosixPath('myFirstDirectory/')
c = 'mySecondDirectory'
d = 'myThirdDirectory'
e = 'myExecutable.exe'
print(a / b / c / d / e)
# Result
c:/myFirstDirectory/mySecondDirectory/myThirdDirectory/myExecutable.exe
I used this when I needed a user to provide the location of an assets directory and my code was looking up using windows path strings
In [1]: from pathlib import Path, PureWindowsPath
In [2]: USER_ASSETS_DIR = Path('/asset/dir') # user provides this form environment variable
In [3]: SPECIFIC_ASSET = PureWindowsPath('some\asset')
In [4]: USER_ASSETS_DIR / SPECIFIC_ASSET
Out[4]: PosixPath('/asset/dir/some/asset')
You can also do this:
import re
a = 'c:/'
b = 'myFirstDirectory/'
c = 'mySecondDirectory'
d = 'myThirdDirectory'
e = 'myExecutable.exe'
joined = os.path.join(a, b, c, d, e)
formatted = re.sub(r'/|\', re.escape(os.sep), joined)
This is going to switch all your potentially mixed slashes into OS compliant ones.
I know it’s an ancient topic but I couldn’t resist. 🙂
Postgres command client psql
doesn’t accept back slashes even on Windows:
>psql -U user -h 111.111.111.111 -d mydb
psql (12.2, server 12.5 . . .
. . .
mydb=> i C:mypathmyscript.sql
C:: Permission denied
So needed to fix it when executing from Python 3.8.6
. Didn’t want to resort to naive string replacement and used existing function:
script_path = Path(script_dir).resolve()
input_sql = f'\i {script_path.joinpath("myscript.sql").as_posix()}n'
But under the hood it has:
# ...ProgramsPythonPython38Libpathlib.py
def as_posix(self):
"""Return the string representation of the path with forward (/)
slashes."""
f = self._flavour
return str(self).replace(f.sep, '/')
The way I do it is fairly straightforward: rstrip
all the paths from their slashes, regardless of quantity and correctness, add join those paths back using the correct separator.
import os
def join_path_regardless_of_separators(*paths):
return os.path.sep.join(path.rstrip(r"/") for path in paths)
a = 'c:/'
b = 'myFirstDirectory/'
c = 'mySecondDirectory'
d = 'myThirdDirectory\\\/'
e = 'myExecutable.exe'
join_path_regardless_of_separators(a, b, c, d, e)
>>> 'c:\myFirstDirectory\mySecondDirectory\myThirdDirectory\myExecutable.exe'
Another way to use it, for the same result:
join_path_regardless_of_separators(*"""c:////\\
myFirstDirectory/
mySecondDirectory\\
myThirdDirectory/////
myExecutable.exe
""".split())