2012-01-13 9 views
5

Me dan una lista de rutas que necesito para verificar los archivos dentro. Por supuesto, si se me proporciona una raíz y un subdirectorio, no es necesario procesar el subdirectorio. Por ejemplo¿Cómo determinar si una ruta es un subdirectorio de otra?

c:\test // process this 
c:\test\pics // do not process this 
c:\test2 // process this 

¿Cómo puedo saber (multiplataforma) que un camino no es un subdirectorio del otro. Preferentemente, me gustaría que fuera multiplataforma, y ​​no me preocupan los enlaces simbólicos, siempre que no sean cíclicos (en el peor de los casos, termino procesando los datos dos veces).

ACTUALIZACIÓN: aquí está el código que terminé usando, gracias a @FJ

def unique_path_roots(paths): 
    visited = set() 
    paths = list(set(paths)) 

    for path in sorted(paths,key=cmp_to_key(locale.strcoll)): 
     path = normcase(normpath(realpath(path))) 

     head, tail = os.path.split(path) 
     while head and tail: 
      if head in visited: 
       break 
      head, tail = os.path.split(head) 
     else: 
      yield path 
      visited.add(path) 

Respuesta

6

Me gustaría mantener un conjunto de directorios que ya se ha procesado, y luego para cada nuevo cheque camino para ver si alguno de sus directorios padre ya existen en ese conjunto antes de su procesamiento:

import os.path 

visited = set() 
for path in path_list: 
    head, tail = os.path.split(path) 
    while head and tail: 
     if head in visited: 
      break 
     head, tail = os.path.split(head) 
    else: 
     process(path) 
     visited.add(path) 

Tenga en cuenta que path_list deben ser ordenados de manera que los subdirectorios son siempre después de que sus directorios padre si es que existen.

+0

Esto será más rápido que mi sugerencia, ya que no establece pruebas de adhesión en lugar de escanear una lista. Me gusta. – kindall

+0

@ F.J parece ser un bucle infitie, la cabeza se reduce a c: \ en su base y nunca se establece en Ninguno. – esac

+0

@esac - Disculpa por eso, pensé que en el caso base pondría todo en la cola, no en la cabeza. Ver mi edición, que debería solucionar el problema. –

2

Rastree los directorios que ya ha procesado (en forma normalizada) y no los procese nuevamente si ya los ha visto. Algo como esto debería funcionar:

from os.path import realpath, normcase, sep 

dirs = [r"C:\test", r"C:\test\pics", r"C:\test2"] 

processed = [] 

for dir in dirs: 
    dir = normcase(realpath(dir)) + sep 
    if not any(dir.startswith(p) for p in processed): 
     processed.append(dir) 
     process(dir)   # your code here 
+1

'prefijo común ([r'C: \ test2 ', r'C: \ test']) -> 'C: \\ test'' –

+0

Sí, suspiro. Eso realmente no hace lo que debería, en mi humilde opinión. Lo cambié para hacer un simple 'startswith()' - eso estará bien ya que está normalizado. – kindall

+0

Sí, realmente creo que el comportamiento del prefijo común es algo extraño, parece que solo debería verificarse en los saltos de directorio, ya que proviene del módulo 'os.path', bueno. –

8
def is_subdir(path, directory): 
    path = os.path.realpath(path) 
    directory = os.path.realpath(directory) 

    relative = os.path.relpath(path, directory) 

    if relative.startswith(os.pardir): 
     return False 
    else: 
     return True 
+1

os.sep es la fuente de error :) –

+0

El uso de 'relpath' puede fallar en ms-windows, cuando se intenta encontrar' C: \ foo' relativo a 'D: \ bar'. – ideasman42

-1

fijos y simplificados jgoeders 's versión:

def is_subdir(suspect_child, suspect_parent): 
    suspect_child = os.path.realpath(suspect_child) 
    suspect_parent = os.path.realpath(suspect_parent) 

    relative = os.path.relpath(suspect_child, start=suspect_parent) 

    return not relative.startswith(os.pardir) 
+0

¿Por qué no solo edita la publicación en lugar de agregar otra respuesta duplicada? – jgoeders

+0

Lo siento, publico en SO una vez en una luna azul, y perdí esta característica. –

+0

'realpath' - la eliminación de enlaces simbólicos aquí es problemática. (en muchos casos, no es lo que desea), ya que los siguientes enlaces pueden cambiar completamente el diseño de la ruta. – ideasman42

0

Aquí es una función de utilidad is_subdir me ocurrió.

  • Python3.x compatible (trabaja con bytes y str, igualando os.path que también es compatible con ambos).
  • Normaliza las rutas de comparación.
    (jerarquía principal y caso a trabajar en ms-windows).
  • Evita el uso de os.path.relpath que generará una excepción en ms-windows si las rutas están en unidades diferentes. (C:\foo ->D:\bar)

Código:

def is_subdir(path, directory): 
    """ 
    Returns true if *path* in a subdirectory of *directory*. 
    """ 
    import os 
    from os.path import normpath, normcase, sep 
    path = normpath(normcase(path)) 
    directory = normpath(normcase(directory)) 
    if len(path) > len(directory): 
     sep = sep.encode('ascii') if isinstance(directory, bytes) else sep 
     if path.startswith(directory.rstrip(sep) + sep): 
      return True 
    return False 
Cuestiones relacionadas