Estoy tratando de escribir un script Python simple que copiará un index.tpl a index.html en todos los subdirectorios (con algunas excepciones).
Me estoy estancando al tratar de obtener la lista de subdirectorios.
Estoy tratando de escribir un script Python simple que copiará un index.tpl a index.html en todos los subdirectorios (con algunas excepciones).
Me estoy estancando al tratar de obtener la lista de subdirectorios.
Respuestas:
Hice algunas pruebas de velocidad en varias funciones para devolver la ruta completa a todos los subdirectorios actuales.
tl; dr:
siempre use scandir
:
list_subfolders_with_paths = [f.path for f in os.scandir(path) if f.is_dir()]
Bonificación: con scandir
usted también solo puede obtener nombres de carpetas utilizando en f.name
lugar de f.path
.
Esto (así como todas las demás funciones a continuación) no utilizará la ordenación natural . Esto significa que los resultados se ordenarán así: 1, 10, 2. Para obtener una clasificación natural (1, 2, 10), consulte https://stackoverflow.com/a/48030307/2441026
Resultados :
scandir
es: 3x más rápido que walk
, 32x más rápido que listdir
(con filtro), 35x más rápido que Pathlib
y 36x más rápido que listdir
y 37x (!) Más rápido que glob
.
Scandir: 0.977
Walk: 3.011
Listdir (filter): 31.288
Pathlib: 34.075
Listdir: 35.501
Glob: 36.277
Probado con W7x64, Python 3.8.1. Carpeta con 440 subcarpetas.
En caso de que se pregunte si listdir
podría acelerarse al no hacer os.path.join () dos veces, sí, pero la diferencia es básicamente inexistente.
Código:
import os
import pathlib
import timeit
import glob
path = r"<example_path>"
def a():
list_subfolders_with_paths = [f.path for f in os.scandir(path) if f.is_dir()]
# print(len(list_subfolders_with_paths))
def b():
list_subfolders_with_paths = [os.path.join(path, f) for f in os.listdir(path) if os.path.isdir(os.path.join(path, f))]
# print(len(list_subfolders_with_paths))
def c():
list_subfolders_with_paths = []
for root, dirs, files in os.walk(path):
for dir in dirs:
list_subfolders_with_paths.append( os.path.join(root, dir) )
break
# print(len(list_subfolders_with_paths))
def d():
list_subfolders_with_paths = glob.glob(path + '/*/')
# print(len(list_subfolders_with_paths))
def e():
list_subfolders_with_paths = list(filter(os.path.isdir, [os.path.join(path, f) for f in os.listdir(path)]))
# print(len(list(list_subfolders_with_paths)))
def f():
p = pathlib.Path(path)
list_subfolders_with_paths = [x for x in p.iterdir() if x.is_dir()]
# print(len(list_subfolders_with_paths))
print(f"Scandir: {timeit.timeit(a, number=1000):.3f}")
print(f"Listdir: {timeit.timeit(b, number=1000):.3f}")
print(f"Walk: {timeit.timeit(c, number=1000):.3f}")
print(f"Glob: {timeit.timeit(d, number=1000):.3f}")
print(f"Listdir (filter): {timeit.timeit(e, number=1000):.3f}")
print(f"Pathlib: {timeit.timeit(f, number=1000):.3f}")
import os
def get_immediate_subdirectories(a_dir):
return [name for name in os.listdir(a_dir)
if os.path.isdir(os.path.join(a_dir, name))]
¿Por qué nadie ha mencionado glob
? glob
le permite usar la expansión de nombre de ruta de estilo Unix, y es mi función para casi todo lo que necesita encontrar más de un nombre de ruta. Lo hace muy fácil:
from glob import glob
paths = glob('*/')
Tenga en cuenta que glob
devolverá el directorio con la barra diagonal final (como lo haría Unix), mientras que la mayoría de las path
soluciones basadas omitirán la barra diagonal final.
paths = [ p.replace('/', '') for p in glob('*/') ]
.
[p[:-1] for p in paths]
, ya que ese método de reemplazo también reemplazará las barras diagonales escapadas en el nombre del archivo (no es que sean comunes).
rstrip
lugar de strip
, ya que este último convertirá las rutas totalmente calificadas en rutas relativas.
strip('/')
eliminará tanto el inicio como el final '/', rstrip('/')
eliminará solo el final
Marque " Obtener una lista de todos los subdirectorios en el directorio actual ".
Aquí hay una versión de Python 3:
import os
dir_list = next(os.walk('.'))[1]
print(dir_list)
(s.rstrip("/") for s in glob(parent_dir+"*/"))
es más eficiente en el tiempo. Mi sospecha intuitiva es que una solución stat()
basada debería ser profundamente más rápida que el glob de estilo shell. Lamentablemente, me falta la voluntad y realmente lo descubro. os.walk()
timeit
import os, os.path
Para obtener subdirectorios inmediatos (ruta completa) en un directorio:
def SubDirPath (d):
return filter(os.path.isdir, [os.path.join(d,f) for f in os.listdir(d)])
Para obtener el último (más nuevo) subdirectorio:
def LatestDirectory (d):
return max(SubDirPath(d), key=os.path.getmtime)
list( filter(...) )
.
os.walk
Es tu amigo en esta situación.
Directamente de la documentación:
walk () genera los nombres de archivo en un árbol de directorios, caminando el árbol de arriba hacia abajo o de abajo hacia arriba. Para cada directorio en el árbol enraizado en la parte superior del directorio (incluida la parte superior), produce una tupla de 3 (dirpath, dirnames, nombres de archivo).
Este método lo hace todo de una vez.
from glob import glob
subd = [s.rstrip("/") for s in glob(parent_dir+"*/")]
Usando el módulo FilePath de Twisted:
from twisted.python.filepath import FilePath
def subdirs(pathObj):
for subpath in pathObj.walk():
if subpath.isdir():
yield subpath
if __name__ == '__main__':
for subdir in subdirs(FilePath(".")):
print "Subdirectory:", subdir
Dado que algunos comentaristas han preguntado cuáles son las ventajas de usar las bibliotecas de Twisted para esto, iré un poco más allá de la pregunta original aquí.
Hay una documentación mejorada en una rama que explica las ventajas de FilePath; tal vez quieras leer eso.
Más específicamente en este ejemplo: a diferencia de la versión de biblioteca estándar, esta función se puede implementar sin importar . La función "subdire" es totalmente genérica, en el sentido de que opera únicamente con su argumento. Para copiar y mover los archivos utilizando la biblioteca estándar, debe depender de " open
" incorporado listdir
",", quizás " isdir
" o " os.walk
" o " shutil.copy
". Quizás " os.path.join
" también. Sin mencionar el hecho de que necesita una cadena pasó un argumento para identificar el archivo real. Echemos un vistazo a la implementación completa que copiará el "index.tpl" de cada directorio a "index.html":
def copyTemplates(topdir):
for subdir in subdirs(topdir):
tpl = subdir.child("index.tpl")
if tpl.exists():
tpl.copyTo(subdir.child("index.html"))
La función "subdire" anterior puede funcionar en cualquier FilePath
objeto similar. Lo que significa, entre otras cosas, ZipPath
objetos. Lamentablemente, ZipPath
es de solo lectura en este momento, pero podría ampliarse para admitir la escritura.
También puede pasar sus propios objetos con fines de prueba. Para probar las API que usan os.path sugeridas aquí, debe usar los nombres importados y las dependencias implícitas y, en general, realizar magia negra para que sus pruebas funcionen. Con FilePath, haces algo como esto:
class MyFakePath:
def child(self, name):
"Return an appropriate child object"
def walk(self):
"Return an iterable of MyFakePath objects"
def exists(self):
"Return true or false, as appropriate to the test"
def isdir(self):
"Return true or false, as appropriate to the test"
...
subdirs(MyFakePath(...))
Acabo de escribir un código para mover máquinas virtuales vmware, y terminé usando os.path
y shutil
para realizar la copia de archivos entre subdirectorios.
def copy_client_files (file_src, file_dst):
for file in os.listdir(file_src):
print "Copying file: %s" % file
shutil.copy(os.path.join(file_src, file), os.path.join(file_dst, file))
No es terriblemente elegante, pero funciona.
Aquí hay una manera:
import os
import shutil
def copy_over(path, from_name, to_name):
for path, dirname, fnames in os.walk(path):
for fname in fnames:
if fname == from_name:
shutil.copy(os.path.join(path, from_name), os.path.join(path, to_name))
copy_over('.', 'index.tpl', 'index.html')
Tengo que mencionar la biblioteca path.py , que uso con mucha frecuencia.
Obtener los subdirectorios inmediatos se vuelve tan simple como eso:
my_dir.dirs()
El ejemplo de trabajo completo es:
from path import Path
my_directory = Path("path/to/my/directory")
subdirs = my_directory.dirs()
NB: my_directory todavía se puede manipular como una cadena, ya que Path es una subclase de cadena, pero proporciona un montón de métodos útiles para manipular rutas
def get_folders_in_directories_recursively(directory, index=0):
folder_list = list()
parent_directory = directory
for path, subdirs, _ in os.walk(directory):
if not index:
for sdirs in subdirs:
folder_path = "{}/{}".format(path, sdirs)
folder_list.append(folder_path)
elif path[len(parent_directory):].count('/') + 1 == index:
for sdirs in subdirs:
folder_path = "{}/{}".format(path, sdirs)
folder_list.append(folder_path)
return folder_list
La siguiente función se puede llamar como:
get_folders_in_directories_recursively (directorio, índice = 1) -> da la lista de carpetas en el primer nivel
get_folders_in_directories_recursively (directorio) -> da todas las subcarpetas
import glob
import os
def child_dirs(path):
cd = os.getcwd() # save the current working directory
os.chdir(path) # change directory
dirs = glob.glob("*/") # get all the subdirectories
os.chdir(cd) # change directory to the script original location
return dirs
La child_dirs
función toma una ruta en un directorio y devuelve una lista de los subdirectorios inmediatos en él.
dir
|
-- dir_1
-- dir_2
child_dirs('dir') -> ['dir_1', 'dir_2']
import pathlib
def list_dir(dir):
path = pathlib.Path(dir)
dir = []
try:
for item in path.iterdir():
if item.is_dir():
dir.append(item)
return dir
except FileNotFoundError:
print('Invalid directory')
Un revestimiento usando pathlib:
list_subfolders_with_paths = [p for p in pathlib.Path(path).iterdir() if p.is_dir()]