chris
chris

Reputation: 4996

Python renaming files being weird

I have this function:

def rename(path):
    """ Renames all the files to be cropped-%d.xxx """
    count = 0
    for thing in os.listdir(path):
        root, ext = os.path.splitext(thing)
        os.rename(os.path.join(path, thing), os.path.join(path, sys.argv[1]+".cropped{0}".format(count)+ext))
        count += 1

This works, but if I run it on a directory which I have already run it on, it seems to delete half of the files its renaming. Why would this happen?

Upvotes: 3

Views: 102

Answers (2)

dano
dano

Reputation: 94871

It's easier to see what's happening if you print out the renaming you're doing:

def rename(path):
    """ Renames all the files to be cropped-%d.xxx """
    count = 0 
    for thing in os.listdir(path):
        root, ext = os.path.splitext(thing)
        old = os.path.join(path, thing)
        new = os.path.join(path, sys.argv[1] + ".cropped{0}".format(count) + ext)
        print("rename from {} to {}".format(old, new))
        os.rename(old, new)
        count += 1

Here's sample output running twice:

dan@dandesk:~$ python ok.py b
rename from test/4.txt to test/b.cropped0.txt
rename from test/2.txt to test/b.cropped1.txt
rename from test/5.txt to test/b.cropped2.txt
rename from test/7.txt to test/b.cropped3.txt
rename from test/1.txt to test/b.cropped4.txt
rename from test/3.txt to test/b.cropped5.txt
rename from test/6.txt to test/b.cropped6.txt
dan@dandesk:~$ python ok.py b
rename from test/b.cropped3.txt to test/b.cropped0.txt
rename from test/b.cropped4.txt to test/b.cropped1.txt
rename from test/b.cropped2.txt to test/b.cropped2.txt
rename from test/b.cropped5.txt to test/b.cropped3.txt
rename from test/b.cropped1.txt to test/b.cropped4.txt
rename from test/b.cropped0.txt to test/b.cropped5.txt
rename from test/b.cropped6.txt to test/b.cropped6.txt

And now here's the test directory contents:

dan@dandesk:~$ ls -1 test
b.cropped2.txt
b.cropped3.txt
b.cropped4.txt
b.cropped5.txt
b.cropped6.txt

As you can see, b.cropped1.txt and b.cropped0.txt have disappeared. The output above reveals why:

rename from test/b.cropped3.txt to test/b.cropped0.txt
rename from test/b.cropped4.txt to test/b.cropped1.txt
...
rename from test/b.cropped1.txt to test/b.cropped4.txt
rename from test/b.cropped0.txt to test/b.cropped5.txt

We rename two files to the missing names, and then rename them again later, which means we lose whatever was in b.cropped4.txt and b.cropped5.txt.

You can avoid this by making sure the new name you're going to use doesn't already exist, and if it does, increment count until you get a file that doesn't exist:

def rename(path):
    """ Renames all the files to be cropped-%d.xxx """
    count = 0
    for thing in os.listdir(path):
        root, ext = os.path.splitext(thing)
        old = os.path.join(path, thing)
        while True:
            new = os.path.join(path, sys.argv[1] + ".cropped{0}".format(count) + ext)
            if not os.path.exists(new):
                break
            count += 1
        print("rename from {} to {}".format(old, new))
        os.rename(old, new)
        count += 1

Output:

dan@dandesk:~$ python ok.py b
rename from test/4.txt to test/b.cropped0.txt
rename from test/2.txt to test/b.cropped1.txt
rename from test/5.txt to test/b.cropped2.txt
rename from test/1.txt to test/b.cropped3.txt
rename from test/3.txt to test/b.cropped4.txt
rename from test/6.txt to test/b.cropped5.txt
dan@dandesk:~$ python ok.py b
rename from test/b.cropped3.txt to test/b.cropped6.txt
rename from test/b.cropped4.txt to test/b.cropped7.txt
rename from test/b.cropped2.txt to test/b.cropped8.txt
rename from test/b.cropped5.txt to test/b.cropped9.txt
rename from test/b.cropped1.txt to test/b.cropped10.txt
rename from test/b.cropped0.txt to test/b.cropped11.txt

Upvotes: 3

GreenAsJade
GreenAsJade

Reputation: 14685

It would happen when a file with the new name already exists, so you are replacing an existing file with a renamed one, losing the original in the process.

Upvotes: 0

Related Questions