2

I want to write a script that receives a path to a directory and a path to a file contained in that directory (possibly nested many directories deep) and returns a path to this file relative to the outer directory.

For example, if the outer directory is /home/hugomg/foo and the inner file is /home/hugomg/foo/bar/baz/unicorns.txt I would like the script to output bar/baz/unicorns.txt.

Right now I am doing it using realpath and string manipulation:

import os

dir_path = "/home/hugomg/foo"
file_path = "/home/hugomg/foo/bar/baz/unicorns.py"

dir_path = os.path.realpath(dir_path)
file_path = os.path.realpath(file_path)

if not file_path.startswith(dir_path):
    print("file is not inside the directory")
    exit(1)

output = file_path[len(dir_path):]
output = output.lstrip("/")
print(output)

But is there a more robust way to do this? I'm not confident that my current solution is the right way to do this. Is using startswith together with realpath a correct way to test that one file is inside another? And is there a way to avoid that awkward situation with the leading slash that I might need to remove?

hugomg
  • 68,213
  • 24
  • 160
  • 246

1 Answers1

1

You can use the commonprefix and relpath of the os.path module to find the longest common prefix of two paths. Its always preferred to use realpath.

import os
dir_path = os.path.realpath("/home/hugomg/foo")
file_path = os.path.realpath("/home/hugomg/foo/bar/baz/unicorns.py")
common_prefix = os.path.commonprefix([dir_path,file_path])

if common_prefix != dir_path:
    print("file is not inside the directory")
    exit(1)
print(os.path.relpath(file_path, dir_path))

Output:

bar/baz/unicorns.txt
Taku
  • 31,927
  • 11
  • 74
  • 85
  • This also feels off... lstrip treats its parameter as a set of characters to remove, not as a prefix to remove. And does this still work if `dir_path` and `file_path` are not normalized, absolute path names? – hugomg Apr 11 '17 at 01:52
  • perhaps `relpath` is more suited? – Taku Apr 11 '17 at 02:14
  • Looks like [this question](http://stackoverflow.com/questions/7287996/python-get-relative-path-from-comparing-two-absolute-paths/7288019#7288019) is close to what I was asking. BTW, someone there pointed out that apparently commonprefix is deprecated in favor of the commonpath function. – hugomg Apr 11 '17 at 02:28