90

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/mySecondDirectory\myThirdDirectory\myExecutable.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 Files\Autodesk\Maya2013.5\bin
C:\Program Files\Autodesk\Maya2013.5\mentalray\scripts\AETemplates
C:\Program Files\Autodesk\Maya2013.5\Python
C:\Program Files\Autodesk\Maya2013.5\Python\lib\site-packages
C:\Program Files\Autodesk\Maya2013.5\bin\python26.zip\lib-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 Files\Nuke7.0v4\lib\site-packages
C:\Program Files\Nuke7.0v4/plugins/modules
nookie
  • 997
  • 2
  • 7
  • 8
  • `os` is clever and works out which slashes you need for your OS. To use it correctly, don't put slashes in your strings a,b,c,d and e - os will add them. – ejrb May 02 '13 at 08:40
  • 1
    I see, but what if I get mixed slashes when asking for a path? (I have updated the first post with an example) – nookie May 02 '13 at 08:46
  • 10
    you can use `os.path.normpath(mixed_slashes_path)` and get the slashes normalized. Oh and I'm guessing you're doing this inside of Maya; it does things very much UNIX-like, hence the slashes in there in the paths that it's adding for you. – dash-tom-bang Jul 28 '15 at 20:07

9 Answers9

71

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.

user3666197
  • 1
  • 6
  • 50
  • 92
Maximus
  • 1,244
  • 10
  • 12
  • 8
    Would it be better to just import posixpath instead of os.path and then do `posixpath.join(a, b, c, d, e)` which will always give you forward slashes. – semicolon Oct 14 '14 at 20:56
  • @semicolon, isn't posixpath the same as importing os.path? In any case, on my windows machine `posixpath.join()` still returns mixed slashes. – Maximus Oct 16 '14 at 06:48
  • If you look [here](https://docs.python.org/2/library/os.path.html), os.path will import one of posixpath, ntpath, macpath or os2emxpath based on your os. I am surprised you still get mixed slashes with posixpath. I just tested it on a windows machine and `os.path.join('foo', 'bar')` gave me `'a\\b'` and `posixpath.join('foo', 'bar')` gave me `'a/b'`. – semicolon Oct 16 '14 at 20:11
  • I should have clarified that `os.path.join()` (and the module it imports) does not replace slashes within the strings it is joining, which may lead to mixed slashes (as is happening in the question). – Maximus Oct 18 '14 at 12:21
  • That is true, but it seemed as though the only existing slashes within the strings it was joining were forward slashes, and as posixpath also uses forward slashes you would not get any mixed slashes using posixpath in the OP's scenario. – semicolon Oct 20 '14 at 17:23
  • Even preferable I would say would be to use `os.abspath()` to normalize all sorts of things. Perhaps there's something in posixpath that'll suit? – dash-tom-bang May 19 '15 at 02:10
  • This approach adds a bug if the code ever runs on something Unix-ish. IOW it puts a time bomb in your code, triggered by "code gets ported" - which is exactly the situation where you have too many problems from deep down in the code already. – toolforger Apr 17 '18 at 11:58
  • For anyone (like me) who was wondering, "Why the heck the double back-slashes!?" ("\\" instead of "\"), see here: https://stackoverflow.com/a/501197/4561887 – Gabriel Staples Aug 23 '18 at 03:59
  • The correct **cross-platfom** method is to use `os.path.normpath()`. On Windows, it'll normalise the path to using backward slashes. If you have to have forward slashes, then you are probably dealing with URLs and not paths, so use URL-specific methods such as `urllib.parse.urljoin()` (requires actual URLs, not just a path). – Martijn Pieters Nov 26 '18 at 15:35
  • Mixed slashes is *not actually a problem* as Windows is happy to process either, even if mixed. – Martijn Pieters Nov 26 '18 at 15:36
  • I work in a wsl env, so I need double backslash at begin and then forward slashes. I do `local_path = '\\\\' + path.join(home_w, repo).split('\\\\')[1].replace('\\', '/')`. Result: `\\wsl$/debian/home../` – Timo Mar 08 '21 at 16:36
43

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.

Honest Abe
  • 8,430
  • 4
  • 49
  • 64
pyrocumulus
  • 9,072
  • 2
  • 43
  • 53
  • 1
    Thanks for your answer but what if I get slashes in one string (when having two or more folders)? I have updated the first post with some paths I get from sys.path – nookie May 02 '13 at 08:57
  • 1
    It depends on where that path is coming from. But most of the time, you should already have those slashes in the correct format. That is, if you get the path through Python. If you have some external source you do not control and that source provides forward slashes instead of backward slashes; you might want to fix that up first. – pyrocumulus May 02 '13 at 09:02
  • So I should check the string afterwards and make sure that the format is correct? – nookie May 02 '13 at 09:09
  • No I would check your external input (the input you apparently do not control the format of) **before** putting it in `os.path.join`. This way you make sure that `os.path.join` does not make bad decisions based on possibly bad input. – pyrocumulus May 02 '13 at 09:12
  • I don't see the danger of having bad input here, in the sense of "bad" meaning "having mixed separators". IMO Windows allows happily for mixing `/` and ``\``, so no problem here. – glglgl May 02 '13 at 09:45
  • No real danger but if you want your code to be cross-platform (as he is suggesting), it's better to program on the defensive side and sanitize your input beforehand. Although that could be classified as an opinion rather than a fact, of course ;) – pyrocumulus May 02 '13 at 09:49
  • 1
    Splitting out the root directory and re-joining will not yield the correct result; the root of `C:` needs to be specified. See http://stackoverflow.com/questions/2422798/. – dash-tom-bang May 19 '15 at 02:08
  • Thanks @dash-tom-bang, I updated the answer accordingly. Strange that nobody has noticed this before, could it be version related or something? – pyrocumulus May 19 '15 at 06:55
  • There is no need to remove slashes. **Just use the standard `os.path.normpath()` function** to normalise paths if you are worried about mixed slashes in paths. There is no need to avoid them anyway, as Windows is happy to accept paths with mixed slashes. – Martijn Pieters Nov 26 '18 at 15:38
  • My issue, is the os.getcwd() conflicts, so there has to be a reason behind that convention of best choice that join() makes, but getcwd() does not have a alternative. try: import posixpath ; print(posixpath.join(*os.getcwd().split('\\'))) # may work for some of us? –  Feb 10 '20 at 14:43
  • `as dash-tom-bang states, apparently for Windows you do need to include a separator`: this is done with your `os.sep` I think. – Timo Jun 18 '21 at 08:40
18

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:\myFirstDirectory\mySecondDirectory\myThirdDirectory\myExecutable.exe

Process finished with exit code 0
Amit Pathak
  • 1,145
  • 11
  • 26
Andre Odendaal
  • 759
  • 7
  • 7
  • 6
    `os.path.abspath()` calls `os.path.normpath()` first. You can achieve the same here, just call `os.path.normpath()`. – Martijn Pieters Nov 26 '18 at 15:36
  • The problem remains as Windows cannot process the backslash correctly and think it is the escape char. – Timo Mar 08 '21 at 16:33
9

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:\www\app\my/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)
    
oriadam
  • 7,747
  • 2
  • 50
  • 48
  • 3
    Note that if your string happen to use ` \ ` to escape spaces inside folders, this method will break. For example `"/usr/my\ files"` will translate to `"usr","my"," files"` instead of `"usr","my files"` – oriadam Jul 17 '16 at 10:47
  • 9
    Don't re-invent the wheel. Just use `os.path.normpath()`. – Martijn Pieters Nov 26 '18 at 15:36
8

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')
dinosaurwaltz
  • 1,731
  • 1
  • 14
  • 12
2

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:\myFirstDirectory\mySecondDirectory\myThirdDirectory\myExecutable.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.

ejrb
  • 338
  • 3
  • 9
  • os.path.isdir(os.path.join('C:','Users','nookie')) returns False. I didn't use any Linux Os to add my folders, those came just from software installation! – nookie May 02 '13 at 09:08
  • 1
    What version of Python are you using, @ejrb? Because on mine, I don't get the first slash. (I see `C:myFirstDirectory\mySecondDirectory...`) – dash-tom-bang May 19 '15 at 02:11
1

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. :)

ashrasmun
  • 490
  • 6
  • 11
1

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:\my\path\myscript.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:

# ...\Programs\Python\Python38\Lib\pathlib.py
    def as_posix(self):
        """Return the string representation of the path with forward (/)
        slashes."""
        f = self._flavour
        return str(self).replace(f.sep, '/')
Nick Legend
  • 789
  • 1
  • 7
  • 21
0

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())
Guimoute
  • 4,407
  • 3
  • 12
  • 28