Решением, позволяющим избежать всех этих исключений, является использование shutil.move
shutil.move(filename, bak_filename)
Если место назначения уже существует, но не является каталогом, оно может бытьперезаписывается в зависимости от семантики os.rename ().
Если место назначения находится в текущей файловой системе, тогда используется os.rename (). В противном случае src копируется в dst с помощью copy_function, а затем удаляется. В случае символических ссылок, новая символическая ссылка, указывающая на цель src, будет создана в или как dst, и src будет удален.
Так что это в основном делает то, что вы пытаетесь сделать, но вбиблиотека, доступная на всех дистрибутивах Python.
Обратите внимание, что производительность может быть плохой, если файл большой и целевой файл существует , а os.rename
отказывается перезаписывать его (полностью зависитв операционной системе, но, например, Windows будет отказываться переименовывать поверх существующего файла), потому что откат, когда os.rename
выбрасывает OSError
, копирует источник , а затем удаляет. Реализация не пытается удалить файл , а затем переименовать снова, потому что если rename
не удается, Python предполагает, что мы пытаемся переименовать через файловые системы, и в этом случае порядок копирования + удаления в порядке(именно так работает Unix mv
).
try:
os.rename(src, real_dst)
except OSError:
if os.path.islink(src):
...
else:
copy_function(src, real_dst)
os.unlink(src)
Чтобы обойти это возможное существование файла, можно выполнить предыдущий вызов os.remove
, заключенный в оператор try/except OSError
.
try:
os.remove(bak_filename)
except OSError:
pass
shutil.move(filename, bak_filename) # or os.rename(filename, bak_filename)
Конечно, если bak_filename
заблокирован / не удаляется, исключение все равно может быть вызвано на shutil.mode
. Также обратите внимание, что если мы попытались удалить целевой файл, os.rename
будет так же хорошо, как shutil.move
. Если целевой файл не может быть удален, операция все равно не может быть успешной.