shutil - Operazioni di Alto Livello su File.
Scopo: Operazioni di alto livello su file.
Il modulo shutil include operazioni di alto livello su file tipo la copiatura e l'archiviazione.
Copiare i File
copyfile()
copia il contenuto della sorgente nella destinazione e solleva IOError
se non si possiedono i permessi per scrivere nel file di destinazione.
# shutil_copyfile.py
import glob
import shutil
print('PRIMA:', glob.glob('shutil_copyfile.*'))
shutil.copyfile('shutil_copyfile.py', 'shutil_copyfile.py.copy')
print('DOPO:', glob.glob('shutil_copyfile.*'))
Visto che la funzione apre il file di input per leggerlo, a prescindere dal tipo, i file speciali (tipo i nodi di dispositivo di Unix) non possono essere copiati come nuovi file speciali con copyfile()
.
$ python3 shutil_copyfile.py PRIMA: ['shutil_copyfile.py'] DOPO: ['shutil_copyfile.py', 'shutil_copyfile.py.copy']
L'implementazione di copyfile()
usa la funzione a basso livello copyfileobj()
. Mentre gli argomenti per copyfile()
sono nomi di file, quelli per copyfileobj()
sono handle di file aperti. Il terzo argomento (opzionale) è la lunghezza del buffer da usare per leggere porzioni di file.
# shutil_copyfileobj.py
import io
import os
import shutil
import sys
class VerboseStringIO(io.StringIO):
def read(self, n=-1):
next = io.StringIO.read(self, n)
print('letti({}) ottenuti {} byte'.format(n, len(next)))
return next
lorem_ipsum = '''Lorem ipsum dolor sit amet, consectetuer
adipiscing elit. Vestibulum aliquam mollis dolor. Donec
vulputate nunc ut diam. Ut rutrum mi vel sem. Vestibulum
ante ipsum.'''
print('Predefinito:')
input = VerboseStringIO(lorem_ipsum)
output = io.StringIO()
shutil.copyfileobj(input, output)
print()
print('Tutto in una volta:')
input = VerboseStringIO(lorem_ipsum)
output = io.StringIO()
shutil.copyfileobj(input, output, -1)
print()
print('Blocchi di 256 byte:')
input = VerboseStringIO(lorem_ipsum)
output = io.StringIO()
shutil.copyfileobj(input, output, 256)
Il comportamento predefinito è quello di leggere usando grandi blocchi. Si usa -1
per leggere tutto l'input in una volta oppure un intero positivo per impostare la dimensione del blocco desiderata. Questo esempio usa diverse dimensioni di blocco per mostrare l'effetto.
$ python3 shutil_copyfileobj.py Predefinito: letti(16384) ottenuti 166 byte letti(16384) ottenuti 0 byte Tutto in una volta: letti(-1) ottenuti 166 byte letti(-1) ottenuti 0 byte Blocchi di 256 byte: letti(256) ottenuti 166 byte letti(256) ottenuti 0 byte
La funzione copy()
interpreta il nome destinazione come lo strumento da riga di comando di Unix cp
. Se la destinazione designata corrisponde a una directory invece che a un file, viene creato un nuovo file nella directory usando il nome base della sorgente.
# shutil_copy.py
import glob
import os
import shutil
os.mkdir('example')
print('PRIMA:', glob.glob('example/*'))
shutil.copy('shutil_copy.py', 'example')
print('DOPO :', glob.glob('example/*'))
I permessi per il file sono copiati assieme al contenuto.
$ python3 shutil_copy.py PRIMA: [] DOPO : ['example/shutil_copy.py']
copy2()
funziona come copy()
, ma include data/ora di accesso e modifica nei meta-dati copiati nel nuovo file.
# shutil_copy2.py
import os
import shutil
import time
def show_file_info(filename):
stat_info = os.stat(filename)
print(' Modalità :', oct(stat_info.st_mode))
print(' Creato :', time.ctime(stat_info.st_ctime))
print(' Accesso :', time.ctime(stat_info.st_atime))
print(' Modificato:', time.ctime(stat_info.st_mtime))
os.mkdir('example')
print('SORGENTE:')
show_file_info('shutil_copy2.py')
shutil.copy2('shutil_copy2.py', 'example')
print('DESTINAZIONE:')
show_file_info('example/shutil_copy2.py')
Il nuovo file ha tutte le stesse caratteristiche della vecchia versione.
$ python3 shutil_copy2.py SORGENTE: Modalità : 0o100600 Creato : Fri Feb 17 22:37:07 2017 Accesso : Fri Feb 17 22:37:07 2017 Modificato: Fri Feb 17 22:37:07 2017 DESTINAZIONE: Modalità : 0o100600 Creato : Fri Feb 17 22:37:56 2017 Accesso : Fri Feb 17 22:37:07 2017 Modificato: Fri Feb 17 22:37:07 2017
Copiare i Meta-dati del file
In modalità predefinita in Unix, quando viene creato un nuovo file, esso riceve i permessi in base alla umask dell'utente corrente. Per copiare i permessi da un file a un altro si usa copymode()
.
# shutil_copymode.py
import os
import shutil
import subprocess
with open('file_da_cambiare.txt', 'wt') as f:
f.write('contenuto')
os.chmod('file_da_cambiare.txt', 0o444)
print('PRIMA:', oct(os.stat('file_da_cambiare.txt').st_mode))
shutil.copymode('shutil_copymode.py', 'file_da_cambiare.txt')
print('DOPO :', oct(os.stat('file_da_cambiare.txt').st_mode))
Questo script crea un file da modificare, poi usa copymode()
per duplicare i permessi dello script al file di esempio.
$ python3 shutil_copymode.py PRIMA: 0o100444 DOPO : 0o100600
Per copiare altri meta-dati per un file si usa copystat()
.
# shutil_copystat.py
import os
import shutil
import time
def show_file_info(filename):
stat_info = os.stat(filename)
print(' Modalità :', oct(stat_info.st_mode))
print(' Creato :', time.ctime(stat_info.st_ctime))
print(' Accesso :', time.ctime(stat_info.st_atime))
print(' Modificato:', time.ctime(stat_info.st_mtime))
with open('file_da_cambiare.txt', 'wt') as f:
f.write('contenuto')
os.chmod('file_da_cambiare.txt', 0o444)
print('PRIMA:')
show_file_info('file_da_cambiare.txt')
shutil.copystat('shutil_copystat.py', 'file_da_cambiare.txt')
print('DOPO:')
show_file_info('file_da_cambiare.txt')
Con copystat()
, solo i permessi e le date associate al file sono duplicati.
$ python3 shutil_copystat.py PRIMA: Modalità : 0o100444 Creato : Fri Feb 17 22:46:51 2017 Accesso : Fri Feb 17 22:46:22 2017 Modificato: Fri Feb 17 22:46:51 2017 DOPO: Modalità : 0o100600 Creato : Fri Feb 17 22:46:51 2017 Accesso : Fri Feb 17 22:46:50 2017 Modificato: Fri Feb 17 22:46:50 2017
Lavorare con Alberi di Directory
Il modulo shutil comprende 3 funzioni per lavorare con alberi di directory. Per copiare una directory da un posto a un altro si usa copytree()
. Attraversa ricorsivamente l'albero di directory sorgente, copiandone i file nella destinazione. La directory destinazione non deve essere già esistente.
# shutil_copytree.py
import glob
import pprint
import shutil
print('PRIMA:')
pprint.pprint(glob.glob('/tmp/example/*'))
shutil.copytree('../shutil', '/tmp/example')
print('\nDOPO:')
pprint.pprint(glob.glob('/tmp/example/*'))
L'argomento symlinks
controlla se i collegamenti simbolici debbano essere copiati come collegamenti o come file. Il comportamento predefinito è di copiare il contenuto su nuovi file. Se l'opzione è True
vengono creati dei nuovi collegamenti simbolici nella destinazione.
$ python3 shutil_copytree.py PRIMA: [] DOPO: ['/tmp/example/example', '/tmp/example/example.out', '/tmp/example/file_to_change.txt', '/tmp/example/index.rst', '/tmp/example/shutil_copy.py', '/tmp/example/shutil_copy2.py', '/tmp/example/shutil_copyfile.py', '/tmp/example/shutil_copyfile.py.copy', '/tmp/example/shutil_copyfileobj.py', '/tmp/example/shutil_copymode.py', '/tmp/example/shutil_copystat.py', '/tmp/example/shutil_copytree.py', '/tmp/example/shutil_copytree_verbose.py', '/tmp/example/shutil_disk_usage.py', '/tmp/example/shutil_get_archive_formats.py', '/tmp/example/shutil_get_unpack_formats.py', '/tmp/example/shutil_make_archive.py', '/tmp/example/shutil_move.py', '/tmp/example/shutil_rmtree.py', '/tmp/example/shutil_unpack_archive.py', '/tmp/example/shutil_which.py', '/tmp/example/shutil_which_regular_file.py']
copytree()
accetta due argomenti chiamabili per controllare il suo comportamento. L'argomento ignore
viene chiamato con il nome di ciascuna directory o sotto directory che viene copiata, assieme a una lista del contenuto della directory. Dovrebbe ritornare un elenco di elementi che dovrebbero essere copiati. L'argomento copy_function
viene chiamato per eseguire realmente la copia dei file.
# shutil_copytree_verbose.py
import glob
import pprint
import shutil
def verbose_copy(src, dst):
print('in copia\n {!r}\n verso {!r}'.format(src, dst))
return shutil.copy2(src, dst)
print('PRIMA:')
pprint.pprint(glob.glob('/tmp/example/*'))
print()
shutil.copytree(
'../shutil', '/tmp/example',
copy_function=verbose_copy,
ignore=shutil.ignore_patterns('*.py'),
)
print('\nDOPO:')
pprint.pprint(glob.glob('/tmp/example/*'))
Nell'esempio, ignore_patterns()
viene usata per creare una funzione chiamata come parametro per l'argomento ignore
per evitare la copia dei file sorgente Python. verbose_copy()
stampa i nomi dei file che sono copiati, quindi usa copy2()
, la funzione di copia predefinita, per eseguire le copie.
$ python3 shutil_copytree_verbose.py PRIMA: [] in copia '../shutil/index.rst' verso '/tmp/example/index.rst' in copia '../shutil/example.out' verso '/tmp/example/example.out' in copia '../shutil/file_to_change.txt' verso '/tmp/example/file_to_change.txt' in copia '../shutil/example' verso '/tmp/example/example' in copia '../shutil/shutil_copyfile.py.copy' verso '/tmp/example/shutil_copyfile.py.copy' DOPO: ['/tmp/example/file_to_change.txt', '/tmp/example/shutil_copyfile.py.copy', '/tmp/example/example.out', '/tmp/example/example', '/tmp/example/index.rst']
Per eliminare una directory e il suo contenuto, si usa rmtree()
.
# shutil_rmtree.py
import glob
import pprint
import shutil
print('PRIMA:')
pprint.pprint(glob.glob('/tmp/example/*'))
shutil.rmtree('/tmp/example')
print('\nDOPO:')
pprint.pprint(glob.glob('/tmp/example/*'))
Gli errori sono sollevati come eccezioni in modalità predefinita e possono essere ignorati se il secondo argomento è True
, e una funzione speciale per la gestione dell'errore può essere fornita come terzo parametro.
$ python3 shutil_rmtree.py PRIMA: ['/tmp/example/file_to_change.txt', '/tmp/example/shutil_copyfile.py.copy', '/tmp/example/example.out', '/tmp/example/example', '/tmp/example/index.rst'] DOPO: []
Per spostare un file o una directory da un posto all'altro si usa move()
.
# shutil_move.py
import glob
import shutil
with open('example.txt', 'wt') as f:
f.write('contents')
print('PRIMA: ', glob.glob('example*'))
shutil.move('example.txt', 'example.out')
print('DOPO : ', glob.glob('example*'))
La semantica è simile a quella del comando Unix mv
. Se la sorgente e la destinazione sono all'interno dello stesso file system, la sorgente viene rinominata. Altrimenti la sorgente viene copiata nella destinazione, quindi la sorgente viene rimossa.
$ python3 shutil_move.py PRIMA: ['example.txt'] DOPO : ['example.out']
Trovare File
La funzione which()
esamina un percorso di ricerca per trovare un file. Il caso d'uso tipico è cercare un programma eseguibile nel percorso di ricerca della shell definito nella variabile di ambiente PATH
.
# shutil_which.py
import shutil
print(shutil.which('virtualenv'))
print(shutil.which('pip'))
print(shutil.which('nessun-programma'))
Se non è stata trovata corrispondenza con i parametri di ricerca, which()
ritorna None
.
$ python3 shutil_which.py /usr/local/bin/virtualenv /usr/local/bin/pip None
which()
riceve gli argomenti da filtrare in base ai permessi che ha il file, e il percorso di ricerca da esaminare. L'argomento path
è predefinito a os.environ('PATH')
, ma può essere qualsiasi stringa che contenga nomi di directory separate da os.pathsep
. L'argomento mode
dovrebbe essere una bitmask che corrisponde ai permessi del file. Nella modalità predefinita la bitmask corrisponde a file eseguibili, ma l'esempio seguente usa una bitmask per cercare file leggibili e un percorso di ricerca alternativo per trovare un file di configurazione.
# shutil_which_regular_file.py
import os
import shutil
path = os.pathsep.join([
'.',
os.path.expanduser('~/pymotw'),
])
mode = os.F_OK | os.R_OK
filename = shutil.which(
'config.ini',
mode=mode,
path=path,
)
print(filename)
Esiste comunque una race condition quando si cercano file leggibili in questo modo, in quanto nel tempo trascorso tra il cercare il file e il reale tentativo di usarlo, il file stesso potrebbe essere stato cancellato o i suoi permessi modificati.
$ touch config.ini $ python3 shutil_which_regular_file.py ./config.ini
Archivi
La libreria standard di Python comprende molti moduli per gestire file di archivio come tarfile e zipfile. Ci sono anche parecchie funzioni di più alto livello per creare ed estrarre archivi in shutil. get_archive_formats()
ritorna una sequenza di nomi e descrizioni per i formati supportati nel sistema corrente.
# shutil_get_archive_formats.py
import shutil
for format, description in shutil.get_archive_formats():
print('{:<5}: {}'.format(format, description))
I formati supportati dipendono da quali moduli e librerie sottostanti sono disponibili, quindi il risultato di questo esempio può variare.
$ python3 shutil_get_archive_formats.py bztar: bzip2'ed tar-file gztar: gzip'ed tar-file tar : uncompressed tar file xztar: xz'ed tar-file zip : ZIP file
Si usa made_archive()
per creare un nuovo file archivio. I suoi input sono progettati per supportare al meglio l'archiviazione di una intera directory e del suo contenuto, ricorsivamente. Nella modalità predefinita usa la directory di lavoro corrente, in modo che tutti i suoi file e le sotto directory appaiano al livello superiore dell'archivio. Per modificare questo comportamento si usa l'argomento root_dir
per spostarsi su di una nuova posizione relativa nel file system e base_dir
per specificare una directory da aggiungere all'archivio.
# shutil_make_archive.py
import logging
import shutil
import sys
import tarfile
logging.basicConfig(
format='%(message)s',
stream=sys.stdout,
level=logging.DEBUG,
)
logger = logging.getLogger('pymotw')
print('Creazione archivio:')
shutil.make_archive(
'example', 'gztar',
root_dir='..',
base_dir='shutil',
logger=logger,
)
print('\nContenuto archivio:')
with tarfile.open('example.tar.gz', 'r') as t:
for n in t.getnames():
print(n)
Questo esempio inizia all'interno della directory degli esempi per shutil e si sposta su di un livello nel file system, poi aggiunge la directory shutil
a un archivio tar compresso con gzip. Il modulo logging è configurato per mostrare i messaggi da make_archive()
.
$ python3 shutil_make_archive.py Creazione archivio: changing into '..' Creating tar archive changing back to '...' Contenuto archivio: shutil shutil/shutil_disk_usage.py shutil/shutil_copyfileobj.py shutil/index.rst shutil/shutil_rmtree.py shutil/shutil_copy2.py shutil/shutil_get_archive_formats.py shutil/shutil_which.py shutil/shutil_copyfile.py shutil/example.out shutil/shutil_copytree_verbose.py shutil/shutil_copymode.py shutil/file_to_change.txt shutil/example shutil/shutil_move.py shutil/shutil_copystat.py shutil/shutil_copytree.py shutil/shutil_which_regular_file.py shutil/shutil_get_unpack_formats.py shutil/shutil_copy.py shutil/shutil_unpack_archive.py shutil/shutil_copyfile.py.copy shutil/shutil_make_archive.py
shutil
mantene un registro dei formati nel sistema corrente, accessibili tramite get_unpack_formats()
.
# shutil_get_unpack_formats.py
import shutil
for format, exts, description in shutil.get_unpack_formats():
print('{:<5}: {}, nomi che finiscono in {}'.format(
format, description, exts))
Il registro è diverso da quello per la creazione degli archivi visto che include anche estensioni comuni di file utilizzate da ciascun formato in modo che la funzione per estrarre un archivio possa indovinare quale formato usare in base all'estensione del file.
$ python3 shutil_get_unpack_formats.py bztar: bzip2'ed tar-file, nomi che finiscono in ['.tar.bz2', '.tbz2'] gztar: gzip'ed tar-file, nomi che finiscono in ['.tar.gz', '.tgz'] tar : uncompressed tar file, nomi che finiscono in ['.tar'] xztar: xz'ed tar-file, nomi che finiscono in ['.tar.xz', '.txz'] zip : ZIP file, nomi che finiscono in ['.zip']
L'archivio si estrae con unpack_archive()
passando il nome del file archivio e opzionalmente la directory nella quale dovrebbe avvenire l'estrazione. Se nessuna directory viene passata, viene usata la directory corrente.
# shutil_unpack_archive.py
import pathlib
import shutil
import sys
import tempfile
with tempfile.TemporaryDirectory() as d:
print('Estrazione archivio:')
shutil.unpack_archive(
'example.tar.gz',
extract_dir=d,
)
print('\nCreato:')
prefix_len = len(d) + 1
for extracted in pathlib.Path(d).rglob('*'):
print(str(extracted)[prefix_len:])
In questo esempio unpack_archive()
è in grado di determinare il formato dell'archivio visto che il nome del file finisce per tar.gz
, e quel valore è associato al formato gztar
nel registro dei formati di estrazione.
$ python3 shutil_unpack_archive.py Estrazione archivio: Creato: shutil shutil/shutil_which.py shutil/shutil_copy.py shutil/shutil_get_archive_formats.py shutil/shutil_unpack_archive.py shutil/file_to_change.txt shutil/shutil_copytree_verbose.py shutil/shutil_copyfile.py.copy shutil/shutil_move.py shutil/shutil_copystat.py shutil/example.out shutil/example shutil/shutil_rmtree.py shutil/shutil_disk_usage.py shutil/shutil_make_archive.py shutil/shutil_copyfile.py shutil/shutil_copymode.py shutil/shutil_copy2.py shutil/shutil_which_regular_file.py shutil/shutil_copytree.py shutil/shutil_get_unpack_formats.py shutil/index.rst shutil/shutil_copyfileobj.py
Spazio nel File System
Può essere utile esaminare il file system locale per vedere quanto spazio sia disponibile prima di eseguire una operazione che richiede molto tempo che potrebbe esaurire quello spazio. disk_usage()
ritorna una tupla con lo spazio totale, quello attualmente in uso, e il rimanente libero.
# shutil_disk_usage.py
import shutil
total_b, used_b, free_b = shutil.disk_usage('.')
gib = 2 ** 30 # GiB == gibibyte
gb = 10 ** 9 # GB == gigabyte
print('Totale: {:6.2f} GB {:6.2f} GiB'.format(
total_b / gb, total_b / gib))
print('Usato : {:6.2f} GB {:6.2f} GiB'.format(
used_b / gb, used_b / gib))
print('Libero: {:6.2f} GB {:6.2f} GiB'.format(
free_b / gb, free_b / gib))
I valori ritornati da disk_usage()
sono in byte, quindi il programma di esempio converte i valori in una unità di misura maggiormente leggibile prima di stamparli.
$ python3 shutil_disk_usage.py Totale: 19.55 GB 18.21 GiB Usato : 16.70 GB 15.55 GiB Libero: 1.84 GB 1.71 GiB
Vedere anche:
- shutil
- La documentazione della libreria standard per questo modulo.