Question

I wonder if python provides a canonical way to copy a file to a directory with its original leading directories appended, like cp --parents does. From cp man page :

`--parents'
[...]
     cp --parents a/b/c existing_dir

 copies the file `a/b/c' to `existing_dir/a/b/c', creating any
 missing intermediate directories.

I haven't seen anything in shutil documentation that refers to this. Of course, I could create the whole directory structure in existing_dir directory before copying any file to it, but it is perhaps overhead to do this.

Était-ce utile?

La solution

I finally came up with the following code. It acts almost exactly as cp --parents does.

import os, shutil

def cp_parents(target_dir, files):
    dirs = []
    for file in files:
        dirs.append(os.path.dirname(file))
    dirs.sort(reverse=True)
    for i in range(len(dirs)):
        if not dirs[i] in dirs[i-1]:
            need_dir = os.path.normpath(target_dir + dirs[i])
            print("Creating", need_dir )
            os.makedirs(need_dir)
    for file in files:
        dest = os.path.normpath(target_dir + file)
        print("Copying %s to %s" % (file, dest))
        shutil.copy(file, dest)

Call it like this:

target_dir = '/tmp/dummy'
files = [ '/tmp/dir/file1', '/tmp/dir/subdir/file2', '/tmp/file3' ]

cp_parents(target_dir, files)

Output is:

Creating /tmp/dummy/tmp/dir/subdir
Copying /tmp/dir/file1 to /tmp/dummy/tmp/dir/file1
Copying /tmp/dir/subdir/file2 to /tmp/dummy/tmp/dir/subdir/file2
Copying /tmp/file3 to /tmp/dummy/tmp/file3

There is probably a better way to handle this, but it works.

Autres conseils

Not sure on your specific requirements, but sounds like shutil.copytree will be right for you. You can see the full docs here, but basically all you need to call in your example is something like

shutil.copytree( 'a/b/c', 'existing_dir/a/b/c' )
import os
import shutil

files = [ '/usr/bin/ls','/etc/passwd' , '/var/log/daily.out' , '/var/log/system.log' , '/var/log/asl/StoreData' ]

def copy_structure(dst_dir,source_files):
    if not os.path.exists(dst_dir) and os.path.isdir(dst_dir):
        os.mkdir(dst_dir)
    for file in source_files:
        dir_name = os.path.dirname(file)
        final_dir_path = os.path.normpath(dst_dir+dir_name)
        if not os.path.exists(final_dir_path):
            os.makedirs(final_dir_path)            
        if os.path.exists(file): 
            shutil.copy(file,final_dir_path)

copy_structure('/tmp/backup',files)  

ONLY 3.6 or higher (it uses f function)

This is my implementation of cp --parents

First gets the offsets for getting the src directories and the filename, also, for the directory offset (sometimes i wanted to omit the first folder of the src file)

Then it extracts the folders and the filename (the check in the src_dirs is because i noticed that when src file wasnt nested in any folder it crashed)

Finally it creates the directory tree into the destination folder and copies the file into it

def __copy_parents(src, dest_folder, dir_offset=0):
    ''' Copies src tree into dest, offset (optional) omits n folders of the src path'''
    prev_offset = 0 if dir_offset == 0 else src.replace('/', '%', dir_offset - 1).find('/') + 1
    post_offset = src.rfind('/')

    src_dirs = '' if post_offset == -1 else src[prev_offset:post_offset]
    src_filename = src[post_offset + 1:]

    os.makedirs(f'{dest_folder}/{src_dirs}', exist_ok=True)
    shutil.copy(src, f'{dest_folder}/{src_dirs}/{src_filename}')
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top