89

Is it possible to force a rename os.rename to overwrite another file if it already exists? For example in the code below if the file Tests.csv already exists it would be replaced by the Tests.txt file (that was also renamed to Tests.csv).

os.rename("C:\Users\Test.txt","C:\Users\Tests.csv");
Splashlin
  • 7,225
  • 12
  • 46
  • 50

10 Answers10

82

Since Python 3.3, there is now a standard cross-platform solution, os.replace:

Rename the file or directory src to dst. If dst is a directory, OSError will be raised. If dst exists and is a file, it will be replaced silently if the user has permission. The operation may fail if src and dst are on different filesystems. If successful, the renaming will be an atomic operation (this is a POSIX requirement).

Availability: Unix, Windows.

New in version 3.3.

However, contrary to the documentation, on Windows it's not guaranteed to be atomic (in Python 3.4.4). That's because internally it uses MoveFileEx on Windows, which doesn't make such a guarantee.

atzz
  • 17,507
  • 3
  • 35
  • 35
  • How is MoveFileEx not atomic? – paulm Mar 22 '16 at 12:42
  • 4
    @paulm If you check its MSDN page, it never promises atomicity and even explicitly suggests `MoveFileTransacted` as an alternative. In practice, `MoveFileEx` is atomic on local file systems but non-atomic on network file systems (well, actually it depends on the server). But even for local FSes its atomicity is non-contractual. – atzz Sep 05 '16 at 17:08
  • 1
    Everyone thinks it's atomic, but nobody is willing to bet on it? – cowlinator Feb 12 '21 at 00:44
60

You could try shutil.move():

from shutil import move

move('C:\\Users\\Test.txt', 'C:\\Users\\Tests.csv')

Or os.remove and then shutil.move:

from os import remove
from shutil import move

remove('C:\\Users\\Tests.csv')
move('C:\\Users\\Test.txt', 'C:\\Users\\Tests.csv')
Blender
  • 289,723
  • 53
  • 439
  • 496
  • 1
    @JohnZwinck: nope, this works on Windows while `os.rename` doesn't. See [`shutil.move` source code](http://hg.python.org/cpython/file/9b26fa7f9adf/Lib/shutil.py#l298). – Fred Foo Nov 12 '11 at 20:39
  • 1
    Well, +1 from me for the current answer, although I suspect this may give rise to a race condition. From the other answers, I gather that this in unavoidable on Windows. – Fred Foo Nov 12 '11 at 20:43
  • 1
    the question asks "...if exists...", so remove should be wrapped with if or try logic – denfromufa Oct 17 '14 at 17:28
10

As the documentation says it's impossible to guarantee an atomic renaming operation on Windows if the file exists so what Python does is asking to do the double step os.remove + os.rename yourself, handling potential errors.

On unix systems rename overwrites the destination if exists (because the operation is guaranteed to be atomic).

Note that on windows it's also possible that deleting the destination file will fail even if you have permission because the file may be in use. This is another essential limitation of the windows file system and you have to handle it yourself in the code.

6502
  • 112,025
  • 15
  • 165
  • 265
8

On Unix, if dst exists and is a file, it will be replaced silently if the user has permission. The operation may fail on some Unix flavors if src and dst are on different filesystems. If successful, the renaming will be an atomic operation (this is a POSIX requirement). On Windows, if dst already exists, OSError will be raised even if it is a file; there may be no way to implement an atomic rename when dst names an existing file. http://docs.python.org/library/os.html#os.rename

Ben
  • 16,275
  • 9
  • 45
  • 63
4

The syntax for the accepted answer (for Python >= 3.3):

os.replace("src/file/path", "dst/file/path")

( Documentation: os.replace )

ellockie
  • 3,730
  • 6
  • 42
  • 44
3
import os
try:
  os.rename("/path/input file", "/path/output file")
except FileExistsError:
  os.remove("/path/output file")
  os.rename("/path/input file", "/path/output file")
SL-Pirate
  • 31
  • 2
3

Funny enough, the documentation for os.rename() says it does replace the target on Unix systems, but on Windows it does not. They mention something vague about it being impossible to implement atomic renaming if the destination exists on Windows, which IMO is hardly enough reason not to support it.

You should catch OSError (destination exists on Windows) and remove the destination and try again, I suppose.

John Zwinck
  • 239,568
  • 38
  • 324
  • 436
1

How about use of replace() and shutil.move() ? example:

import os
from shutil import move

filename=r'C:\Users\Test.txt'
move(filename,filename.replace('.txt','.csv')

also you can make it more general for all the files. Thanks

Deepak
  • 51
  • 5
1

Try something like this:

import os

def rename_downloaded_files(download_dir):
    """Renames downloaded files to (file name).

    Args:
        download_dir (str): The path to the download directory.
    """
    for file in os.listdir(download_dir):
        if file.endswith(".yourExtension"):
            continue
        new_file_name = "enter file + extension"
        os.rename(os.path.join(download_dir, file), os.path.join(download_dir, new_file_name), overwrite=True)

if __name__ == "__main__":
    download_dir = "/path/to/download/directory"
    rename_downloaded_files(download_dir)

This code will rename all downloaded files in the download_dir directory to a file of your choosing, overwriting any existing files with the same name. You can change the path to the directory to match your own system.

1

From the Standard Library documentation, “On Windows, if dst already exists, OSError will be raised even if it is a file; there may be no way to implement an atomic rename when dst names an existing file.”

http://docs.python.org/library/os.html#os.rename

So the only solution, unfortunately, would be to change operating systems; Windows simply disallows a rename() atop an existing file.

Brandon Rhodes
  • 83,755
  • 16
  • 106
  • 147