os.path.commonprefix () et os.path.relpath () sont vos amis:
>>> 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
'/'
Vous pouvez ainsi tester si le préfixe commun est l'un des chemins, c'est à dire si l'un des chemins est un ancêtre commun:
paths = […, …, …]
common_prefix = os.path.commonprefix(list_of_paths)
if common_prefix in paths:
…
Vous pouvez ensuite trouver les chemins relatifs:
relative_paths = [os.path.relpath(path, common_prefix) for path in paths]
Vous pouvez même gérer plus de deux chemins, avec cette méthode, et tester si tous les chemins sont tous en dessous de l'un d'eux.
PS : selon l'apparence de vos chemins, vous voudrez peut-être commencer par effectuer une normalisation (cela est utile dans les situations où l'on ne sait pas s'ils se terminent toujours par '/' ou non, ou si certains des chemins sont relatifs). Les fonctions pertinentes incluent os.path.abspath () et os.path.normpath () .
PPS : comme Peter Briggs l'a mentionné dans les commentaires, l'approche simple décrite ci-dessus peut échouer:
>>> os.path.commonprefix(['/usr/var', '/usr/var2/log'])
'/usr/var'
même si ce /usr/var
n'est pas un préfixe commun des chemins. Forcer tous les chemins à se terminer par «/» avant d'appeler commonprefix()
résout ce problème (spécifique).
PPPS : comme bluenote10 l'a mentionné, l'ajout d'une barre oblique ne résout pas le problème général. Voici sa question de suivi: Comment contourner l'erreur du préfixe os.path.commonprefix de Python?
PPPPS : à partir de Python 3.4, nous avons pathlib , un module qui fournit un environnement de manipulation de chemin plus sain . Je suppose que le préfixe commun d'un ensemble de chemins peut être obtenu en obtenant tous les préfixes de chaque chemin (avec PurePath.parents()
), en prenant l'intersection de tous ces ensembles parents et en sélectionnant le préfixe commun le plus long.
PPPPPS : Python 3.5 a introduit une solution appropriée à cette question :,os.path.commonpath()
qui renvoie un chemin valide.
commonprefix
, par exemple, le préfixe commun pour/usr/var/log
et/usr/var2/log
est renvoyé comme/usr/var
- ce qui n'est probablement pas ce à quoi vous vous attendez. (Il est également possible qu'il renvoie des chemins qui ne sont pas des répertoires valides.)['/usr/var1/log/', '/usr/var2/log/']
?os.path.relpath
:Donc, si le chemin relatif commence par
'..'
- cela signifie que le deuxième chemin n'est pas le descendant du premier chemin.En Python3, vous pouvez utiliser
PurePath.relative_to
:la source
os.pardir
est plus robuste que vérifier..
(d'accord, il n'y a pas beaucoup d'autres conventions, cependant).os.relpath
plus puissant car il gère..
etPurePath.relative_to()
ne fonctionne pas? Est-ce que je manque quelque chose?Une autre option est
la source
os.pardir
devant les deux chemins relatifs résultants possibles).Une rédaction de la suggestion de jme, en utilisant pathlib, en Python 3.
la source
dir1.relative_to(dir2)
donnera PosixPath ('.') S'ils sont identiques. Lorsque vous utilisez,if dir2 in dir1.parents
il exclut le cas d'identité. Si quelqu'un compare des chemins et veut s'exécuterrelative_to()
s'ils sont compatibles avec les chemins, une meilleure solution peut êtreif dir2 in (dir1 / 'x').parents
ouif dir2 in dir1.parents or dir2 == dir1
. Ensuite, tous les cas de compatibilité de chemin sont couverts.Pure Python2 sans dep:
la source
cwd
etpath
sont les mêmes. il devrait d'abord vérifier si ces deux sont identiques et renvoyer l'un""
ou l' autre ou"."
Edit: Voir la réponse de jme pour le meilleur moyen avec Python3.
En utilisant pathlib, vous avez la solution suivante:
Disons que nous voulons vérifier si
son
est un descendant deparent
, et que les deux sont desPath
objets. Nous pouvons obtenir une liste des parties du chemin aveclist(parent.parts)
. Ensuite, on vérifie juste que le début du fils est égal à la liste des segments du parent.Si vous voulez obtenir la partie restante, vous pouvez simplement faire
C'est une chaîne, mais vous pouvez bien sûr l'utiliser comme constructeur d'un autre objet Path.
la source
parent in son.parents
, et si c'est le cas, obtenir le reste avecson.relative_to(parent)
.