La copia di file in Python è un’operazione comune e, fortunatamente, il linguaggio offre strumenti robusti e versatili per gestirla. In questa guida tecnica, esploreremo i vari approcci per eseguire un’operazione di python copy file, partendo dalla copia di singoli file fino ad arrivare a scenari più complessi come la copia ricorsiva di directory.
Modulo shutil
: La Scelta Professionale per la Copia di File e Directory
Quando si tratta di operazioni su file e directory di alto livello, il modulo shutil
(shell utilities) è il tuo migliore amico. Offre funzioni che semplificano notevolmente compiti che, altrimenti, richiederebbero molto più codice.
1. Copia di un Singolo File: shutil.copy()
La funzione shutil.copy(src, dst)
è l’opzione più semplice e comune per copiare un file. Copia il contenuto del file sorgente (src
) nel file o directory di destinazione (dst
).
src
: Il percorso del file sorgente da copiare.dst
: Il percorso della destinazione. Può essere un file (il nome del nuovo file) o una directory (in tal caso, il file verrà copiato al suo interno mantenendo il nome originale).
Caratteristiche:
- Copia i permessi del file sorgente.
- Non copia i metadati come l’ora dell’ultima modifica (usa
shutil.copy2()
per questo). - Se
dst
è una directory, il file verrà copiato al suo interno con lo stesso nome. - Se
dst
esiste ed è un file, verrà sovrascritto. Se è una directory, il file sorgente verrà copiato al suo interno.
Esempio Pratico: Copia un file da una posizione all’altra.
import shutil
import os
# Creiamo un file di test
with open("sorgente.txt", "w") as f:
f.write("Questo è il contenuto del file sorgente.")
# 1. Copia il file sorgente in un nuovo file
try:
shutil.copy("sorgente.txt", "destinazione.txt")
print("File 'sorgente.txt' copiato in 'destinazione.txt'")
except Exception as e:
print(f"Errore durante la copia del file: {e}")
# 2. Creiamo una directory di destinazione
os.makedirs("cartella_destinazione", exist_ok=True)
# 3. Copia il file sorgente nella directory di destinazione
try:
shutil.copy("sorgente.txt", "cartella_destinazione/")
print("File 'sorgente.txt' copiato in 'cartella_destinazione/'")
except Exception as e:
print(f"Errore durante la copia del file nella cartella: {e}")
# Pulizia
os.remove("sorgente.txt")
os.remove("destinazione.txt")
os.remove("cartella_destinazione/sorgente.txt")
os.rmdir("cartella_destinazione")
2. Copia di un Singolo File con Metadati: shutil.copy2()
La funzione shutil.copy2(src, dst)
è identica a shutil.copy()
ma con un’aggiunta fondamentale: copia anche i metadati del file, inclusa l’ora dell’ultima modifica, l’ora di accesso e i flag.
Esempio Pratico: Dimostrare la copia dei metadati.
import shutil
import os
import time
# Creiamo un file di test e modifichiamolo per avere un timestamp significativo
with open("sorgente_meta.txt", "w") as f:
f.write("Contenuto per test metadati.")
time.sleep(1) # Aspetta un secondo per avere un timestamp diverso da creazione
os.utime("sorgente_meta.txt", (time.time(), time.time())) # Aggiorna ora di accesso e modifica
# Ottieni l'ora di modifica originale
original_mtime = os.path.getmtime("sorgente_meta.txt")
print(f"Ora di modifica originale: {time.ctime(original_mtime)}")
# Copia con shutil.copy()
shutil.copy("sorgente_meta.txt", "destinazione_copy.txt")
copy_mtime = os.path.getmtime("destinazione_copy.txt")
print(f"Ora di modifica con copy(): {time.ctime(copy_mtime)}")
print(f"I metadati sono gli stessi con copy(): {original_mtime == copy_mtime}\n")
# Copia con shutil.copy2()
shutil.copy2("sorgente_meta.txt", "destinazione_copy2.txt")
copy2_mtime = os.path.getmtime("destinazione_copy2.txt")
print(f"Ora di modifica con copy2(): {time.ctime(copy2_mtime)}")
print(f"I metadati sono gli stessi con copy2(): {original_mtime == copy2_mtime}")
# Pulizia
os.remove("sorgente_meta.txt")
os.remove("destinazione_copy.txt")
os.remove("destinazione_copy2.txt")
Come si può notare dall’output, shutil.copy()
crea un nuovo file con un timestamp di modifica “fresco”, mentre shutil.copy2()
conserva quello del file sorgente.
3. Copia Ricorsiva di Directory: shutil.copytree()
Questa è la funzione per eccellenza quando hai bisogno di copiare un’intera struttura di directory, inclusi tutti i suoi file e sottodirectory.
shutil.copytree(src, dst, ...)
: Copia ricorsivamente l’intera directorysrc
nella directorydst
.src
: La directory sorgente da copiare.dst
: La directory di destinazione. Non deve esistere prima di chiamarecopytree
. Se esiste, verrà generato un errore.dirs_exist_ok=False
(default): SeTrue
, la directory di destinazione può esistere, e i contenuti saranno uniti (ma i file esistenti saranno sovrascritti). Attenzione: Questa opzione è stata aggiunta in Python 3.8. Per versioni precedenti, devi gestire l’esistenza della directory manualmente.ignore
(opzionale): Una funzione richiamabile che permette di escludere file o directory dalla copia (simile a.gitignore
).
Esempio Pratico: Copiare una struttura di cartelle completa.
import shutil
import os
# Creiamo una struttura di directory di test
os.makedirs("sorgente_dir/sottocartella", exist_ok=True)
with open("sorgente_dir/file1.txt", "w") as f:
f.write("File 1")
with open("sorgente_dir/sottocartella/file2.txt", "w") as f:
f.write("File 2")
# Copia ricorsiva della directory
try:
# Se 'destinazione_dir' esiste, la rimuoiviamo prima per evitare errori nelle versioni < 3.8
if os.path.exists("destinazione_dir"):
shutil.rmtree("destinazione_dir")
shutil.copytree("sorgente_dir", "destinazione_dir")
print("Directory 'sorgente_dir' copiata ricorsivamente in 'destinazione_dir'")
# Verifichiamo il contenuto (opzionale)
print("Contenuto di 'destinazione_dir':")
for root, dirs, files in os.walk("destinazione_dir"):
level = root.replace("destinazione_dir", '').count(os.sep)
indent = ' ' * 4 * (level)
print(f'{indent}{os.path.basename(root)}/')
subindent = ' ' * 4 * (level + 1)
for f in files:
print(f'{subindent}{f}')
except Exception as e:
print(f"Errore durante la copia ricorsiva: {e}")
# Pulizia
shutil.rmtree("sorgente_dir")
shutil.rmtree("destinazione_dir")
Copia Ricorsiva con Esclusione di File/Directory:
Supponiamo di voler copiare una directory ma escludere tutti i file .log
e una sottocartella specifica.
import shutil
import os
# Creiamo una struttura di directory più complessa
os.makedirs("progetto_sorgente/data", exist_ok=True)
os.makedirs("progetto_sorgente/logs", exist_ok=True) # Questa cartella verrà esclusa
with open("progetto_sorgente/config.ini", "w") as f: f.write("config")
with open("progetto_sorgente/data/user.csv", "w") as f: f.write("data")
with open("progetto_sorgente/logs/app.log", "w") as f: f.write("log data")
with open("progetto_sorgente/temp.log", "w") as f: f.write("temp log")
def ignore_patterns(path, names):
ignored = []
# Escludi tutti i file .log
for name in names:
if name.endswith('.log'):
ignored.append(name)
# Escludi la directory 'logs'
if 'logs' in names:
ignored.append('logs')
print(f"Ignoro in {path}: {ignored}")
return ignored
try:
if os.path.exists("progetto_destinazione"):
shutil.rmtree("progetto_destinazione")
shutil.copytree("progetto_sorgente", "progetto_destinazione", ignore=ignore_patterns)
print("\nCopia ricorsiva con pattern di esclusione completata.")
print("\nContenuto della destinazione (dovrebbe mancare 'logs/' e i file .log):")
for root, dirs, files in os.walk("progetto_destinazione"):
level = root.replace("progetto_destinazione", '').count(os.sep)
indent = ' ' * 4 * (level)
print(f'{indent}{os.path.basename(root)}/')
subindent = ' ' * 4 * (level + 1)
for f in files:
print(f'{subindent}{f}')
except Exception as e:
print(f"Errore durante la copia con esclusione: {e}")
# Pulizia
shutil.rmtree("progetto_sorgente")
if os.path.exists("progetto_destinazione"):
shutil.rmtree("progetto_destinazione")
4. Copia a Basso Livello: open()
e Lettura/Scrittura
Per scenari molto specifici, o se si vuole un controllo granulare sul processo di copia, è possibile copiare il file leggendo il contenuto in blocchi e scrivendolo. Questo è ciò che le funzioni di shutil
fanno “sotto il cofano”.
Questo approccio è utile per:
- Copiare file molto grandi per evitare di caricare l’intero contenuto in memoria.
- Implementare logica di copia personalizzata (es. visualizzare una barra di progresso, crittografare durante la copia).
Esempio Pratico: Copia un file in blocchi.
import os
def copy_file_manual(src_path, dst_path, buffer_size=4096):
"""
Copia un file leggendolo e scrivendolo in blocchi.
"""
try:
with open(src_path, 'rb') as fsrc: # 'rb' per lettura in modalità binaria
with open(dst_path, 'wb') as fdst: # 'wb' per scrittura in modalità binaria
while True:
buffer = fsrc.read(buffer_size)
if not buffer:
break
fdst.write(buffer)
print(f"File '{src_path}' copiato manualmente in '{dst_path}'")
except FileNotFoundError:
print(f"Errore: Il file sorgente '{src_path}' non trovato.")
except Exception as e:
print(f"Errore durante la copia manuale del file: {e}")
# Creiamo un file di test più grande
with open("large_sorgente.bin", "wb") as f:
f.write(os.urandom(1024 * 1024 * 5)) # 5 MB di dati casuali
copy_file_manual("large_sorgente.bin", "large_destinazione.bin")
# Pulizia
os.remove("large_sorgente.bin")
os.remove("large_destinazione.bin")
L’uso della modalità binaria ('rb'
, 'wb'
) è cruciale per garantire che tutti i byte del file vengano copiati correttamente, specialmente per file non testuali (immagini, video, eseguibili).
Considerazioni Finali sul “Python Copy File”
- Gestione degli Errori: In tutti gli esempi, è fondamentale includere blocchi
try-except
per gestire potenzialiIOError
(es. permessi negati, disco pieno, file non trovato) o altre eccezioni. - Sovrascrittura:
shutil.copy()
eshutil.copy2()
sovrascrivono i file di destinazione se esistono.shutil.copytree()
genera un errore se la directory di destinazione esiste (a meno che non si usidirs_exist_ok=True
in Python 3.8+). - Simbolici Link:
shutil.copy()
eshutil.copy2()
copiano il contenuto di un link simbolico, non il link stesso. Per copiare il link come link, si può usareshutil.copy(src, dst, follow_symlinks=False)
. - Sicurezza: Fai attenzione quando copi file da fonti non fidate, specialmente in posizioni sensibili del sistema, poiché potresti inavvertitamente sovrascrivere file importanti.
In generale, per la maggior parte delle esigenze di python copy file, le funzioni del modulo shutil
sono la scelta consigliata per la loro semplicità, efficienza e gestione integrata di molti dettagli che altrimenti dovrebbero essere gestiti manualmente.