os.path.commonprefix () y os.path.relpath () son tus amigos:
>>> 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
'/'
Por lo tanto, puede probar si el prefijo común es una de las rutas, es decir, si una de las rutas es un antepasado común:
paths = […, …, …]
common_prefix = os.path.commonprefix(list_of_paths)
if common_prefix in paths:
…
Luego puede encontrar las rutas relativas:
relative_paths = [os.path.relpath(path, common_prefix) for path in paths]
Incluso puede manejar más de dos rutas, con este método, y probar si todas las rutas están debajo de una de ellas.
PD : dependiendo de cómo se vean sus rutas, es posible que desee realizar alguna normalización primero (esto es útil en situaciones en las que uno no sabe si siempre terminan con '/' o no, o si algunas de las rutas son relativas). Las funciones relevantes incluyen os.path.abspath () y os.path.normpath () .
PPS : como Peter Briggs mencionó en los comentarios, el enfoque simple descrito anteriormente puede fallar:
>>> os.path.commonprefix(['/usr/var', '/usr/var2/log'])
'/usr/var'
aunque no/usr/var
es un prefijo común de los caminos. Forzar que todas las rutas terminen con '/' antes de llamar resuelve este problema (específico).commonprefix()
PPPS : como se menciona en bluenote10, agregar una barra oblicua no resuelve el problema general. Aquí está su pregunta de seguimiento: ¿Cómo eludir la falacia del prefijo os.path.common de Python?
PPPPS : comenzando con Python 3.4, tenemos pathlib , un módulo que proporciona un entorno de manipulación de ruta más sano. Supongo que el prefijo común de un conjunto de rutas puede obtenerse obteniendo todos los prefijos de cada ruta (con PurePath.parents()
), tomando la intersección de todos estos conjuntos principales y seleccionando el prefijo común más largo.
PPPPPS : Python 3.5 introdujo una solución adecuada a esta pregunta: os.path.commonpath()
que devuelve una ruta válida.
commonprefix
, por ejemplo, el prefijo común para/usr/var/log
y/usr/var2/log
se devuelve como/usr/var
- que probablemente no sea lo que esperaría. (También es posible que devuelva rutas que no son directorios válidos)['/usr/var1/log/', '/usr/var2/log/']
?os.path.relpath
:Entonces, si la ruta relativa comienza con
'..'
- significa que la segunda ruta no es descendiente de la primera ruta.En Python3 puedes usar
PurePath.relative_to
:fuente
os.pardir
es más robusto que verificarlo..
(de acuerdo, sin embargo, no hay muchas otras convenciones).os.relpath
más poderoso ya que lo maneja..
yPurePath.relative_to()
no lo hace? ¿Me estoy perdiendo de algo?Otra opción es
fuente
os.pardir
sin embargo, se puede verificar la presencia frente a las dos posibles rutas relativas resultantes).Una reseña de la sugerencia de jme, usando pathlib, en Python 3.
fuente
dir1.relative_to(dir2)
dará PosixPath ('.') Si son lo mismo. Cuando lo usaif dir2 in dir1.parents
, excluye el caso de identidad. Si alguien está comparando rutas y quiere ejecutarrelative_to()
si son compatibles con rutas, una mejor solución puede serif dir2 in (dir1 / 'x').parents
oif dir2 in dir1.parents or dir2 == dir1
. Entonces, todos los casos de compatibilidad de ruta están cubiertos.Python2 puro sin dep:
fuente
cwd
ypath
son lo mismo. primero debe verificar si esos dos son iguales y regresar""
o bien"."
Editar: Vea la respuesta de jme para la mejor manera con Python3.
Usando pathlib, tiene la siguiente solución:
Digamos que queremos verificar si
son
es un descendiente deparent
, y ambos sonPath
objetos. Podemos obtener una lista de las partes en el camino conlist(parent.parts)
. Luego, solo verificamos que el comienzo del hijo sea igual a la lista de segmentos del padre.Si desea obtener la parte restante, simplemente puede hacer
Es una cadena, pero por supuesto puede usarla como un constructor de otro objeto Path.
fuente
parent in son.parents
, y si es así, obtener el resto conson.relative_to(parent)
.