On Mon, 30 Jan 2017 09:39 pm, Peter Otten wrote: >>>> def rename(source, dest): > ... os.link(source, dest) > ... os.unlink(source) > ... >>>> rename("foo", "baz") >>>> os.listdir() > ['bar', 'baz'] >>>> rename("bar", "baz") > Traceback (most recent call last): > File "<stdin>", line 1, in <module> > File "<stdin>", line 2, in rename > FileExistsError: [Errno 17] File exists: 'bar' -> 'baz'
Thanks Peter! That's not quite ideal, as it isn't atomic: it is possible that the link will succeed, but the unlink won't. But I prefer that over the alternative, which is over-writing a file and causing data loss. So to summarise, os.rename(source, destination): - is atomic on POSIX systems, if source and destination are both on the same file system; - may not be atomic on Windows? - may over-write an existing destination on POSIX systems, but not on Windows; - and it doesn't work across file systems. os.replace(source, destination) is similar, except that it may over-write an existing destination on Windows as well as on POSIX systems. The link/unlink trick: - avoids over-writing existing files on POSIX systems at least; - but maybe not Windows? - isn't atomic, so in the worst case you end up with two links to the one file; - but os.link may not be available on all platforms; - and it won't work across file systems. Putting that all together, here's my attempt at a version of file rename which doesn't over-write existing files: import os import shutil def rename(src, dest): """Rename src to dest only if dest doesn't already exist (almost).""" if hasattr(os, 'link'): try: os.link(src, dest) except OSError: pass else: os.unlink(src) return # Fallback to an implementation which is vulnerable to a # Time Of Check to Time Of Use bug. # Try to reduce the window for this race condition by minimizing # the number of lookups needed between one call and the next. move = shutil.move if not os.file.exists(dest): move(src, dest) else: raise shutil.Error("Destination path '%s' already exists" % dest) Any comments? Any bugs? Any cross-platform way to slay this TOCTOU bug once and for all? -- Steve “Cheer up,” they said, “things could be worse.” So I cheered up, and sure enough, things got worse. -- https://mail.python.org/mailman/listinfo/python-list