Creating a relative symlink in python without using os.chdir()
Asked Answered
P

4

13

Say I have a path to a file:

/path/to/some/directory/file.ext

In python, I'd like to create a symlink in the same directory as the file, that points to the file. I'd like to end up with this:

/path/to/some/directory/symlink -> file.ext

I can do this fairly easily using os.chdir() to cd into the directory and create the symlinks. But os.chdir() is not thread safe, so I'd like to avoid using it. Assuming that the current working directory of the process is not the directory with the file (os.getcwd() != '/path/to/some/directory'), what's the best way to do this?

I guess I could create a busted link in whatever directory I'm in, then move it to the directory with the file:

import os, shutil
os.symlink('file.ext', 'symlink')
shutil.move('symlink', '/path/to/some/directory/.')

Is there a better way to do this?

Note, I don't want to end up with is this:

/path/to/some/directory/symlink -> /path/to/some/directory/file.ext
Pineapple answered 20/3, 2012 at 19:33 Comment(0)
S
18

You could just set the second argument to the destination, like:

import os
os.symlink('file.ext', '/path/to/some/directory/symlink')
Sharkey answered 20/3, 2012 at 19:37 Comment(1)
Just make sure, the link file does not exist, or better, remove it os.remove("/path/to/some/directory/symlink")Toxicant
W
34

You can also use os.path.relpath() so that you can use symlinks with relative paths. Say your script is in a directory foo/ and this directory has subdirectories src/ and dst/, and you want to create relative symlinks in dst/ to point to the files in src/. To do so, you can do:

import os
from glob import glob
for src_path in glob('src/*'):
    os.symlink(
        os.path.relpath(
            src_path,
            'dst/'
        ),
        os.path.join('dst', os.path.basename(src_path))
    )

Listing the contents of dst/ then shows:

1.txt -> ../src/1.txt
2.txt -> ../src/2.txt

Relative symlinks are useful for if you want to create a tarball of the whole foo directory tree, as I don't believe tar updates symlinks to point to the relative path inside of the generated tarball.

Wizardry answered 13/11, 2012 at 0:22 Comment(2)
just a warning: your solution looks good and it impresed me. I tried to apply it in a slightly different situation. relpath seems -- according to other discussions -- not very general. the python relpath function is working properly only for directories, applied to full filepath it does not always produce the expected results (I observe typically that it includes /../.. when only /.. is requred.Leaseback
@Leaseback Had this same problem. The second argument is start, which should be the directory (not the file) the path is relative to os.path.relpath. docs.python.org/3/library/os.path.html#os.path.relpathGrew
S
18

You could just set the second argument to the destination, like:

import os
os.symlink('file.ext', '/path/to/some/directory/symlink')
Sharkey answered 20/3, 2012 at 19:37 Comment(1)
Just make sure, the link file does not exist, or better, remove it os.remove("/path/to/some/directory/symlink")Toxicant
P
1

Nowadays, this can be accomplished using pathlib

from pathlib import Path
target = Path('../target.txt')
my_symlink = Path('symlink.txt')
my_symlink.symlink_to(target)

where target is a relative Path or str.

Platon answered 9/11, 2022 at 21:25 Comment(0)
R
0

python function to create a relative symlink:

import os

def relative_symlink(src, dst):
    dir = os.path.dirname(dst)
    src = os.path.relpath(src, dir)
    return os.symlink(src, dst)

test

os.makedirs("/tmp/srcdir", exist_ok=True)
os.makedirs("/tmp/dstdir", exist_ok=True)
with open("/tmp/srcdir/srcfile", "w") as f:
    f.write("ok\n")
relative_symlink("/tmp/srcdir/srcfile", "/tmp/dstdir/dstfile")
assert os.readlink("/tmp/dstdir/dstfile") == "../srcdir/srcfile"
with open("/tmp/dstdir/dstfile") as f:
    assert f.read() == "ok\n"
os.unlink("/tmp/dstdir/dstfile")
os.rmdir("/tmp/dstdir")
os.unlink("/tmp/srcdir/srcfile")
os.rmdir("/tmp/srcdir")
Rhinestone answered 31/5, 2022 at 19:50 Comment(2)
Shouldn't it be rather Dst = os.path.join(dir, os.path.basename(dst))? Otherwise the file is symlinking to itself.Respondent
So, effectively, Dst is not necessary, as it's equivalent to dstPlaster

© 2022 - 2024 — McMap. All rights reserved.