Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

shutil moving files keeping the same directory structure

Tags:

python

I want to move a lot of files. the path to these files is stored in a list. I want to keep the whole directory structure but want to move them to a different folder.

So for example the files are D:\test\test1\test1.txt D:\test\test1\test2.txt

I want to move them to C:\ from D:\ and keep the directory structure. How should I go about doing it?

this is the code i have, it is not working

import os, fnmatch
import shutil


f=open('test_logs.txt','r') #logs where filenames are stored with filenames as first entry

for line in f:
    filename=line.split()
    output_file="C:" + filename[0].lstrip("D:")
    shutil.move(filename[0],output_file)

I read the file name fine and I can generate the destination filename fine but when I run it, it gives me an error saying "No such file or directory" (and gives the path of the output filename).

like image 915
Illusionist Avatar asked Apr 04 '11 00:04

Illusionist


People also ask

Does Shutil move overwrite?

How do I overwrite a file in Shutil? Use shutil. move() to overwrite a file in Python Call shutil. move(src, dst) with src and dst as full file paths to move the file at src to the destination dst .

How do I copy a folder hierarchy?

Type "xcopy", "source", "destination" /t /e in the Command Prompt window. Instead of “ source ,” type the path of the folder hierarchy you want to copy. Instead of “ destination ,” enter the path where you want to store the copied folder structure. Press “Enter” on your keyboard.

What's the difference between Shutil copy and Shutil copy2?

The shutil. copy2() method is identical to shutil. copy() except that copy2() attempts to preserve file metadata as well.


2 Answers

I think you want something like this:

import sys
import os
import shutil

# terminology:
# path = full path to a file, i.e. directory + file name
# directory = directory, possibly starting with a drive
# file name = the last component of the path

sourcedrive = 'D:'
destdrive = 'C:'

log_list_file = open('test_logs.txt', 'r')
for line in log_list_file:
    sourcepath = line.split()[0]  # XXX is this correct?
    if sourcepath.startswith(sourcedrive):
        destpath = sourcepath.replace(sourcedrive, destdrive, 1)
    else:
        print >>sys.stderr, 'Skipping %s: Not on %s' % (sourcepath, sourcedrive)
        continue

    destdir = os.path.dirname(destpath)    

    if not os.path.isdir(destdir):
        try:
            os.makedirs(destdir)
        except (OSError, IOError, Error) as e:
            print >>sys.stderr, 'Error making %s: %s' % (destdir, e)
            continue

    try:
        shutil.move(sourcepath, destpath)
    except (OSError, IOError, Error) as e:
        print >>sys.stderr, 'Error moving %s to %s: %s' % (sourcepath, destpath, e)

Do you also want to remove the source directory if it's empty?

like image 50
Mikel Avatar answered Nov 14 '22 22:11

Mikel


Update: Ah, ok, I see the problem -- shutil.move won't copy to a nonexistent directory. To do what you're trying to do, you have to create the new directory tree first. Since it's a bit safer to use a built-in move function than to roll your own copy-and-delete procedure, you could do this:

with open('test_logs.txt','r') as f:
    files_to_copy = [line.split()[0] for line in f]
paths_to_copy = set(os.path.split(filename)[0] for filename in files_to_copy)

def ignore_files(path, names, ptc=paths_to_copy):
    return [name for name in names if os.path.join(path, name) not in ptc]

shutil.copytree(src, dst, ignore=ignore_files)

for filename in files_to_copy:
    output_file="C:" + filename.lstrip("D:")
    shutil.move(filename, output_file)

Let me know if that doesn't work


Original Post: If you want to move only some of the files, your best bet is to use shutil.copytree's ignore keyword. Assuming your list of files includes full paths and directories (i.e. ['D:\test\test1\test1.txt', 'D:\test\test1\test2.txt', 'D:\test\test1']), create an ignore_files function and use it like this:

files_to_copy = ['D:\test\test1\test1.txt', 'D:\test\test1\test2.txt', 'D:\test\test1']

def ignore_files(path, names, ftc=files_to_copy):
    return [name for name in names if os.path.join(path, name) not in ftc]

shutil.copytree(src, dst, ignore=ignore_files)

Then you can just delete the files in files_to_copy:

for f in files_to_copy:
    try:
        os.remove(f)
    except OSError:  # can't remove() a directory, so pass
        pass 

I tested this -- make certain that you include the paths you want to copy as well as the files in files_to_copy -- otherwise, this will delete files without copying them.

like image 33
senderle Avatar answered Nov 14 '22 22:11

senderle