web-dev-qa-db-fra.com

Python: comment copier des fichiers rapidement

Il faut au moins 3 fois plus de temps pour copier des fichiers avec shutil.copyfile() que vers un clic droit-copier-coller> clic-droit-coller à l'aide de l'Explorateur de fichiers Windows ou du Finder de Mac. Existe-t-il une alternative plus rapide à shutil.copyfile() en Python? Que pourrait-on faire pour accélérer un processus de copie de fichiers? (La destination des fichiers se trouve sur le lecteur réseau ... si cela fait une différence ...).

MODIFIÉ PLUS TARD:

Voici ce que j'ai fini avec:

def copyWithSubprocess(cmd):        
    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

win=mac=False
if sys.platform.startswith("darwin"):mac=True
Elif sys.platform.startswith("win"):win=True

cmd=None
if mac: cmd=['cp', source, dest]
Elif win: cmd=['xcopy', source, dest, '/K/O/X']

if cmd: copyWithSubprocess(cmd)
17
alphanumeric

La version la plus rapide sans optimisation excessive du code que j'ai avec le code suivant:

class CTError(Exception):
    def __init__(self, errors):
        self.errors = errors

try:
    O_BINARY = os.O_BINARY
except:
    O_BINARY = 0
READ_FLAGS = os.O_RDONLY | O_BINARY
WRITE_FLAGS = os.O_WRONLY | os.O_CREAT | os.O_TRUNC | O_BINARY
BUFFER_SIZE = 128*1024

def copyfile(src, dst):
    try:
        fin = os.open(src, READ_FLAGS)
        stat = os.fstat(fin)
        fout = os.open(dst, WRITE_FLAGS, stat.st_mode)
        for x in iter(lambda: os.read(fin, BUFFER_SIZE), ""):
            os.write(fout, x)
    finally:
        try: os.close(fin)
        except: pass
        try: os.close(fout)
        except: pass

def copytree(src, dst, symlinks=False, ignore=[]):
    names = os.listdir(src)

    if not os.path.exists(dst):
        os.makedirs(dst)
    errors = []
    for name in names:
        if name in ignore:
            continue
        srcname = os.path.join(src, name)
        dstname = os.path.join(dst, name)
        try:
            if symlinks and os.path.islink(srcname):
                linkto = os.readlink(srcname)
                os.symlink(linkto, dstname)
            Elif os.path.isdir(srcname):
                copytree(srcname, dstname, symlinks, ignore)
            else:
                copyfile(srcname, dstname)
            # XXX What about devices, sockets etc.?
        except (IOError, os.error), why:
            errors.append((srcname, dstname, str(why)))
        except CTError, err:
            errors.extend(err.errors)
    if errors:
        raise CTError(errors)

Ce code s'exécute un peu plus lentement que le linux natif "cp -rf".

Par rapport à shutil, le gain pour le stockage local vers tmfps est d'environ 2x-3x et d'environ 6x pour NFS vers le stockage local.

Après le profilage, j'ai remarqué que shutil.copy fait beaucoup de syscals fstat qui sont assez lourds. Si l'on veut optimiser davantage, je suggère de faire un seul fstat pour src et de réutiliser les valeurs. Honnêtement, je ne suis pas allé plus loin car j'ai obtenu presque les mêmes chiffres que l'outil de copie Linux natif et l'optimisation pour plusieurs centaines de millisecondes n'était pas mon objectif.

17
Dmytro

Vous pouvez simplement utiliser le système d'exploitation sur lequel vous effectuez la copie, pour Windows:

from subprocess import call
call(["xcopy", "c:\\file.txt", "n:\\folder\\", "/K/O/X"])

/ K - Copie les attributs. En règle générale, Xcopy réinitialise les attributs en lecture seule
/O - Copie la propriété du fichier et les informations ACL.
/X - Copie les paramètres d'audit de fichier (implique/O).

5
Michael Burns
import sys
import subprocess

def copyWithSubprocess(cmd):        
    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

cmd=None
if sys.platform.startswith("darwin"): cmd=['cp', source, dest]
Elif sys.platform.startswith("win"): cmd=['xcopy', source, dest, '/K/O/X']

if cmd: copyWithSubprocess(cmd)
1
alphanumeric