답변:
os.path.commonprefix () 와 os.path.relpath () 는 당신의 친구입니다 :
>>> print os.path.commonprefix(['/usr/var/log', '/usr/var/security'])
'/usr/var'
>>> print os.path.commonprefix(['/tmp', '/usr/var']) # No common prefix: the root is the common prefix
'/'
따라서 공통 접두사가 경로 중 하나인지, 즉 경로 중 하나가 공통 조상인지 여부를 테스트 할 수 있습니다.
paths = […, …, …]
common_prefix = os.path.commonprefix(list_of_paths)
if common_prefix in paths:
…
그런 다음 상대 경로를 찾을 수 있습니다.
relative_paths = [os.path.relpath(path, common_prefix) for path in paths]
이 방법을 사용하여 둘 이상의 경로를 처리하고 모든 경로가 모두 하나 아래에 있는지 테스트 할 수 있습니다.
추신 : 경로의 모양에 따라 먼저 정규화를 수행하려고 할 수 있습니다 (이것은 항상 '/'로 끝나는 지 아닌지 또는 일부 경로가 상대적인지 모르는 상황에서 유용합니다). 관련 함수에는 os.path.abspath () 및 os.path.normpath ()가 있습니다.
PPS : Peter Briggs가 의견에서 언급했듯이 위에서 설명한 간단한 접근 방식은 실패 할 수 있습니다.
>>> os.path.commonprefix(['/usr/var', '/usr/var2/log'])
'/usr/var'
비록 경로의 일반적인 접두사 /usr/var
가 아닙니다 . 호출하기 전에 모든 경로를 '/'로 commonprefix()
끝내면이 (특정) 문제가 해결됩니다.
PPPS : bluenote10에서 언급했듯이 슬래시를 추가해도 일반적인 문제는 해결되지 않습니다. 그의 후속 질문은 다음과 같습니다. Python의 os.path.commonprefix의 오류를 피하는 방법?
PPPPS : Python 3.4부터는 더 정확한 경로 조작 환경을 제공하는 모듈 인 pathlib가 있습니다. 경로 세트의 공통 접두사는 각 경로의 모든 접두사 ( PurePath.parents()
)를 가져 와서이 모든 부모 세트의 교차점을 취하고 가장 긴 공통 접두사를 선택하여 얻을 수 있다고 생각 합니다.
PPPPPS : Python 3.5는이 질문에 대한 올바른 해결책을 제시했습니다 os.path.commonpath()
. 유효한 경로를 반환합니다.
commonprefix
의 공통 접두사를 예로, /usr/var/log
그리고 /usr/var2/log
반환됩니다 /usr/var
당신이 무엇을 기대할 아마하지 않은 -. (이 유효 디렉토리하지 않은 경로를 반환하는 것도 가능합니다.)
['/usr/var1/log/', '/usr/var2/log/']
?
현재 디렉토리 또는 선택적 시작점에서 상대 경로를 경로로 리턴하십시오.
>>> from os.path import relpath
>>> relpath('/usr/var/log/', '/usr/var')
'log'
>>> relpath('/usr/var/log/', '/usr/var/sad/')
'../log'
따라서 상대 경로로 시작 '..'
하면 두 번째 경로가 첫 번째 경로의 후손이 아님을 의미합니다.
Python3에서는 다음을 사용할 수 있습니다 PurePath.relative_to
.
Python 3.5.1 (default, Jan 22 2016, 08:54:32)
>>> from pathlib import Path
>>> Path('/usr/var/log').relative_to('/usr/var/log/')
PosixPath('.')
>>> Path('/usr/var/log').relative_to('/usr/var/')
PosixPath('log')
>>> Path('/usr/var/log').relative_to('/etc/')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/Cellar/python3/3.5.1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/pathlib.py", line 851, in relative_to
.format(str(self), str(formatted)))
ValueError: '/usr/var/log' does not start with '/etc'
os.pardir
것보다 강력합니다 ..
(그렇지만 다른 규칙은 많지 않습니다).
os.relpath
처리 ..
하고 PurePath.relative_to()
하지 않기 때문에 내가 틀리거나 더 강력 합니까? 뭔가 빠졌습니까?
다른 옵션은
>>> print os.path.relpath('/usr/var/log/', '/usr/var')
log
os.pardir
가능한 두 개의 상대 경로 앞에 존재하는지 확인할 수 있습니다 ).
Python 3에서 pathlib를 사용하여 jme의 제안을 작성했습니다.
from pathlib import Path
parent = Path(r'/a/b')
son = Path(r'/a/b/c/d')
if parent in son.parents or parent==son:
print(son.relative_to(parent)) # returns Path object equivalent to 'c/d'
dir1.relative_to(dir2)
PosixPath을 줄 것이다 ( '.')가 같은 경우. 사용 if dir2 in dir1.parents
하면 ID 케이스가 제외됩니다. 누군가가 경로를 비교하고 경로와 relative_to()
호환되는 경우 실행 하려면 더 나은 솔루션은 if dir2 in (dir1 / 'x').parents
또는 일 수 있습니다 if dir2 in dir1.parents or dir2 == dir1
. 그런 다음 모든 경로 호환성 사례를 다룹니다.
순수 Python2 (dep 없음) :
def relpath(cwd, path):
"""Create a relative path for path from cwd, if possible"""
if sys.platform == "win32":
cwd = cwd.lower()
path = path.lower()
_cwd = os.path.abspath(cwd).split(os.path.sep)
_path = os.path.abspath(path).split(os.path.sep)
eq_until_pos = None
for i in xrange(min(len(_cwd), len(_path))):
if _cwd[i] == _path[i]:
eq_until_pos = i
else:
break
if eq_until_pos is None:
return path
newpath = [".." for i in xrange(len(_cwd[eq_until_pos+1:]))]
newpath.extend(_path[eq_until_pos+1:])
return os.path.join(*newpath) if newpath else "."
cwd
와 path
같은 문제가 있습니다. 그 두 같은과 반환하거나 경우 먼저 확인해야 ""
또는"."
편집 : Python3을 사용하는 가장 좋은 방법은 jme의 답변을 참조하십시오.
pathlib를 사용하면 다음과 같은 솔루션이 있습니다.
son
의 자손 인지 확인 parent
하고 둘 다 Path
객체 인지 확인한다고 가정 해 보겠습니다 . 경로 에서 부품 목록을 얻을 수 있습니다 list(parent.parts)
. 그런 다음 아들의 시작이 부모의 세그먼트 목록과 같은지 확인합니다.
>>> lparent = list(parent.parts)
>>> lson = list(son.parts)
>>> if lson[:len(lparent)] == lparent:
>>> ... #parent is a parent of son :)
남은 부분을 얻으려면 할 수 있습니다.
>>> ''.join(lson[len(lparent):])
문자열이지만 다른 Path 객체의 생성자로 사용할 수도 있습니다.
parent in son.parents
, 그렇다면 나머지를 얻는 것입니다 son.relative_to(parent)
.