Python Shutil.copy if I have a duplicate file will it copy to new location

Jack Walker picture Jack Walker · Oct 22, 2015 · Viewed 12.7k times · Source

I'm working with the shutil.copy method in python.

I found the definition listed below:

def copyFile(src, dest):
    try:
        shutil.copy(src, dest)
    # eg. src and dest are the same file
    except shutil.Error as e:
        print('Error: %s' % e)
    # eg. source or destination doesn't exist
    except IOError as e:
         print('Error: %s' % e.strerror)

I'm accessing the definition inside a loop. The loop is based on a string that changes each time. The code looks at all the files in the directory, and if it sees a part of the string in the file, it copies it to a new location

I am pretty sure that there will be duplicate files. So I was wondering what will happen.

Will they be copied, or will they fail?

Answer

Gall picture Gall · Oct 22, 2015

shutil.copy will not copy the file to a new location, it will overwrite the file.

Copy the file src to the file or directory dst. If dst is a directory, a file with the same basename as src is created (or overwritten) in the directory specified. Permission bits are copied. src and dst are path names given as strings.

So you have to check yourself if the destination file exists and alter the destination as appropriate. For example, this is what you can use to achieve a safe copy:

def safe_copy(file_path, out_dir, dst = None):
    """Safely copy a file to the specified directory. If a file with the same name already 
    exists, the copied file name is altered to preserve both.

    :param str file_path: Path to the file to copy.
    :param str out_dir: Directory to copy the file into.
    :param str dst: New name for the copied file. If None, use the name of the original
        file.
    """
    name = dst or os.path.basename(file_path)
    if not os.path.exists(os.path.join(out_dir, name)):
        shutil.copy(file_path, os.path.join(out_dir, name))
    else:
        base, extension = os.path.splitext(name)
        i = 1
        while os.path.exists(os.path.join(out_dir, '{}_{}{}'.format(base, i, extension))):
            i += 1
        shutil.copy(file_path, os.path.join(out_dir, '{}_{}{}'.format(base, i, extension)))

Here, a '_number' is inserted right before the extension to generate a unique destination name in case of duplicate. Like 'foo_1.txt'.