Sto cercando di scrivere un semplice script Python che copi un index.tpl in index.html in tutte le sottodirectory (con alcune eccezioni).
Mi sto impantanando cercando di ottenere l'elenco delle sottodirectory.
Sto cercando di scrivere un semplice script Python che copi un index.tpl in index.html in tutte le sottodirectory (con alcune eccezioni).
Mi sto impantanando cercando di ottenere l'elenco delle sottodirectory.
Risposte:
Ho fatto alcuni test di velocità su varie funzioni per restituire il percorso completo a tutte le sottodirectory correnti.
tl; dr:
usa sempre scandir
:
list_subfolders_with_paths = [f.path for f in os.scandir(path) if f.is_dir()]
Bonus: Con scandir
te puoi anche semplicemente ottenere i nomi delle cartelle usando f.name
invece di f.path
.
Questo (così come tutte le altre funzioni seguenti) non utilizzerà l'ordinamento naturale . Ciò significa che i risultati verranno ordinati in questo modo: 1, 10, 2. Per ottenere l'ordinamento naturale (1, 2, 10), dai un'occhiata a https://stackoverflow.com/a/48030307/2441026
Risultati :
scandir
è 3 volte più veloce di walk
, 32 volte più veloce di listdir
(con filtro), 35 volte più veloce di Pathlib
e 36 volte più veloce di listdir
e 37x (!) Più veloce di glob
.
Scandir: 0.977
Walk: 3.011
Listdir (filter): 31.288
Pathlib: 34.075
Listdir: 35.501
Glob: 36.277
Testato con W7x64, Python 3.8.1. Cartella con 440 sottocartelle.
Nel caso ti chiedi se listdir
potresti accelerare non facendo os.path.join () due volte, sì, ma la differenza è sostanzialmente inesistente.
Codice:
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))]
Perché nessuno ha menzionato glob
? glob
ti permette di usare l'espansione del percorso in stile Unix, ed è il mio go per funzionare per quasi tutto ciò che ha bisogno di trovare più di un nome di percorso. Lo rende molto semplice:
from glob import glob
paths = glob('*/')
Si noti che glob
restituirà la directory con la barra finale (come farebbe unix) mentre la maggior parte delle path
soluzioni basate ometterà la barra finale.
paths = [ p.replace('/', '') for p in glob('*/') ]
.
[p[:-1] for p in paths]
, poiché quel metodo di sostituzione sostituirà anche qualsiasi barra in avanti evasa nel nome del file (non che quelli siano comuni).
rstrip
invece invece strip
, poiché quest'ultimo trasformerà tutti i percorsi completi in percorsi relativi.
strip('/')
rimuoverà "/" iniziale e finale,rstrip('/')
rimuoverà solo quello finale
Seleziona " Ottenere un elenco di tutte le sottodirectory nella directory corrente ".
Ecco una versione di Python 3:
import os
dir_list = next(os.walk('.'))[1]
print(dir_list)
(s.rstrip("/") for s in glob(parent_dir+"*/"))
è più efficiente in termini di tempo. Il mio intuitivo sospetto è che una soluzione stat()
basata su basi dovrebbe essere profondamente più veloce dei globbing in stile shell. Purtroppo, mi manca la volontà di e davvero scoprire. os.walk()
timeit
import os, os.path
Per ottenere sottodirectory immediate (percorso completo) in una directory:
def SubDirPath (d):
return filter(os.path.isdir, [os.path.join(d,f) for f in os.listdir(d)])
Per ottenere la sottodirectory più recente (più recente):
def LatestDirectory (d):
return max(SubDirPath(d), key=os.path.getmtime)
list( filter(...) )
.
os.walk
è tuo amico in questa situazione.
Direttamente dalla documentazione:
walk () genera i nomi dei file in un albero di directory, camminando l'albero in alto o in basso. Per ogni directory nell'albero radicata nella directory top (incluso top stesso), produce una 3 tupla (dirpath, dirnames, nomi di file).
Questo metodo fa bene tutto in una volta.
from glob import glob
subd = [s.rstrip("/") for s in glob(parent_dir+"*/")]
Utilizzando il modulo FilePath di 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
Poiché alcuni commentatori hanno chiesto quali siano i vantaggi dell'utilizzo delle librerie di Twisted per questo, andrò un po 'oltre la domanda originale qui.
C'è una documentazione migliorata in un ramo che spiega i vantaggi di FilePath; potresti volerlo leggere.
Più specificamente in questo esempio: a differenza della versione standard della libreria, questa funzione può essere implementata senza importazioni . La funzione "subdir" è totalmente generica, in quanto opera solo sul suo argomento. Per copiare e spostare i file utilizzando la libreria standard, è necessario dipendere da " open
" incorporato "," listdir
", forse" isdir
"o" os.walk
"o" shutil.copy
". Forse " os.path.join
" anche. Per non parlare del fatto che è necessario che una stringa abbia passato un argomento per identificare il file effettivo. Diamo un'occhiata all'implementazione completa che copierà "index.tpl" di ciascuna directory in "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 funzione "subdir" sopra può funzionare su qualsiasi FilePath
oggetto simile. Il che significa, tra le altre cose, ZipPath
oggetti. Purtroppo al momento ZipPath
è di sola lettura, ma potrebbe essere esteso per supportare la scrittura.
Puoi anche passare i tuoi oggetti a scopo di test. Per testare le API che utilizzano os.path suggerite qui, devi cercare i nomi importati e le dipendenze implicite ed eseguire generalmente la magia nera per far funzionare i test. Con FilePath, fai qualcosa del genere:
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(...))
Ho appena scritto un po 'di codice per spostare le macchine virtuali VMware e ho finito per usare os.path
e shutil
realizzare la copia dei file tra le sottodirectory.
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))
Non è terribilmente elegante, ma funziona.
Ecco un modo:
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')
Devo menzionare il path.py libreria , che uso molto spesso.
Recuperare le sottodirectory immediate diventa così semplice:
my_dir.dirs()
L'esempio di lavoro completo è:
from path import Path
my_directory = Path("path/to/my/directory")
subdirs = my_directory.dirs()
NB: my_directory può ancora essere manipolata come una stringa, poiché Path è una sottoclasse di string, ma fornisce un sacco di metodi utili per manipolare i percorsi
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 seguente funzione può essere chiamata come:
get_folders_in_directories_recursively (directory, index = 1) -> fornisce l'elenco delle cartelle al primo livello
get_folders_in_directories_recursively (directory) -> fornisce tutte le sottocartelle
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
funzione prende un percorso in una directory e restituisce un elenco delle sottodirectory immediate in essa contenute.
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 liner usando pathlib:
list_subfolders_with_paths = [p for p in pathlib.Path(path).iterdir() if p.is_dir()]