chrisg
chrisg

Reputation: 41705

Why doesn't os.path.join() work in this case?

The below code will not join, when debugged the command does not store the whole path but just the last entry.

os.path.join('/home/build/test/sandboxes/', todaystr, '/new_sandbox/')

When I test this it only stores the /new_sandbox/ part of the code.

Upvotes: 429

Views: 660754

Answers (16)

afghani
afghani

Reputation: 676

Please refer following code snippet for understanding os.path.join(a, b)

a = '/home/user.name/foo/'
b = '/bar/file_name.extension'

print(os.path.join(a, b))
>>> /bar/file_name.extension

OR

a = '/home/user.name/foo'
b = '/bar/file_name.extension'
print(os.path.join(a, b))
>>> /bar/file_name.extension

But, when

a = '/home/user.name/foo/'
b = 'bar/file_name.extension'

print(os.path.join(a, b))
>>> /bar/file_name.extension

OR

a = '/home/user.name/foo'
b = 'bar/file_name.extension'
print(os.path.join(a, b))
>>> /home/user.name/foo/bar/file_name.extension

Upvotes: 1

Trọng Nhân
Trọng Nhân

Reputation: 69

The problem is your laptop maybe running Window. And Window annoyingly use back lash instead of forward slash'/'.
To make your program cross-platform (linux/windows/etc). You shouldn't provide any slashes (forward or backward) in your path if you want os.path.join to handle them properly. you should using:

os.path.join(os.environ.get("HOME"), 'test', 'sandboxes', todaystr, 'new_sandbox')

Or throw some Path(__file__).resolve().parent (path to parent of current file) or anything so that you don't use any slash inside os.path.join

Upvotes: 0

Goblinhack
Goblinhack

Reputation: 3096

os.path.join("a", *"/b".split(os.sep))
'a/b'

a fuller version:

import os

def join (p, f, sep = os.sep):
    f = os.path.normpath(f)
    if p == "":
        return (f);
    else:
        p = os.path.normpath(p)
        return (os.path.join(p, *f.split(os.sep)))

def test (p, f, sep = os.sep):
    print("os.path.join({}, {}) => {}".format(p, f, os.path.join(p, f)))
    print("        join({}, {}) => {}".format(p, f, join(p, f, sep)))

if __name__ == "__main__":
    # /a/b/c for all
    test("\\a\\b", "\\c", "\\") # optionally pass in the sep you are using locally
    test("/a/b", "/c", "/")
    test("/a/b", "c")
    test("/a/b/", "c")
    test("", "/c")
    test("", "c")

Upvotes: 1

Dr Fabio Gori
Dr Fabio Gori

Reputation: 1195

I'd recommend to strip from the second and the following strings the string os.path.sep, preventing them to be interpreted as absolute paths:

first_path_str = '/home/build/test/sandboxes/'
original_other_path_to_append_ls = [todaystr, '/new_sandbox/']
other_path_to_append_ls = [
    i_path.strip(os.path.sep) for i_path in original_other_path_to_append_ls
]
output_path = os.path.join(first_path_str, *other_path_to_append_ls)

Upvotes: -1

suhailvs
suhailvs

Reputation: 21740

you can strip the '/':

>>> os.path.join('/home/build/test/sandboxes/', todaystr, '/new_sandbox/'.strip('/'))
'/home/build/test/sandboxes/04122019/new_sandbox'

Upvotes: 0

openwonk
openwonk

Reputation: 15577

Try combo of split("/") and * for strings with existing joins.

import os

home = '/home/build/test/sandboxes/'
todaystr = '042118'
new = '/new_sandbox/'

os.path.join(*home.split("/"), todaystr, *new.split("/"))


How it works...

split("/") turns existing path into list: ['', 'home', 'build', 'test', 'sandboxes', '']

* in front of the list breaks out each item of list its own parameter

Upvotes: 16

miku
miku

Reputation: 188194

Do not use forward slashes at the beginning of path components, except when refering to the root directory:

os.path.join('/home/build/test/sandboxes', todaystr, 'new_sandbox')

see also: http://docs.python.org/library/os.path.html#os.path.join

Upvotes: 36

Antony Hatchkins
Antony Hatchkins

Reputation: 34044

The idea of os.path.join() is to make your program cross-platform (linux/windows/etc).

Even one slash ruins it.

So it only makes sense when being used with some kind of a reference point like os.environ['HOME'] or os.path.dirname(__file__).

Upvotes: 173

ghammond
ghammond

Reputation: 1082

os.path.join() can be used in conjunction with os.path.sep to create an absolute rather than relative path.

os.path.join(os.path.sep, 'home','build','test','sandboxes',todaystr,'new_sandbox')

Upvotes: 96

Craig McQueen
Craig McQueen

Reputation: 43486

The latter strings shouldn't start with a slash. If they start with a slash, then they're considered an "absolute path" and everything before them is discarded.

Quoting the Python docs for os.path.join:

If a component is an absolute path, all previous components are thrown away and joining continues from the absolute path component.

Note on Windows, the behaviour in relation to drive letters, which seems to have changed compared to earlier Python versions:

On Windows, the drive letter is not reset when an absolute path component (e.g., r'\foo') is encountered. If a component contains a drive letter, all previous components are thrown away and the drive letter is reset. Note that since there is a current directory for each drive, os.path.join("c:", "foo") represents a path relative to the current directory on drive C: (c:foo), not c:\foo.

Upvotes: 568

shacker
shacker

Reputation: 15381

Note that a similar issue can bite you if you use os.path.join() to include an extension that already includes a dot, which is what happens automatically when you use os.path.splitext(). In this example:

components = os.path.splitext(filename)
prefix = components[0]
extension = components[1]
return os.path.join("avatars", instance.username, prefix, extension)

Even though extension might be .jpg you end up with a folder named "foobar" rather than a file called "foobar.jpg". To prevent this you need to append the extension separately:

return os.path.join("avatars", instance.username, prefix) + extension

Upvotes: 0

YOU
YOU

Reputation: 123937

Try with new_sandbox only

os.path.join('/home/build/test/sandboxes/', todaystr, 'new_sandbox')

Upvotes: 5

NuclearPeon
NuclearPeon

Reputation: 6059

To make your function more portable, use it as such:

os.path.join(os.sep, 'home', 'build', 'test', 'sandboxes', todaystr, 'new_sandbox')

or

os.path.join(os.environ.get("HOME"), 'test', 'sandboxes', todaystr, 'new_sandbox')

Upvotes: 10

David Wolever
David Wolever

Reputation: 154682

To help understand why this surprising behavior isn't entirely terrible, consider an application which accepts a config file name as an argument:

config_root = "/etc/myapp.conf/"
file_name = os.path.join(config_root, sys.argv[1])

If the application is executed with:

$ myapp foo.conf

The config file /etc/myapp.conf/foo.conf will be used.

But consider what happens if the application is called with:

$ myapp /some/path/bar.conf

Then myapp should use the config file at /some/path/bar.conf (and not /etc/myapp.conf/some/path/bar.conf or similar).

It may not be great, but I believe this is the motivation for the absolute path behaviour.

Upvotes: 33

ghostdog74
ghostdog74

Reputation: 343141

do it like this, without too the extra slashes

root="/home"
os.path.join(root,"build","test","sandboxes",todaystr,"new_sandbox")

Upvotes: 4

Amber
Amber

Reputation: 527488

It's because your '/new_sandbox/' begins with a / and thus is assumed to be relative to the root directory. Remove the leading /.

Upvotes: 16

Related Questions