Mateen Kiani
Published on Tue Jul 22 2025·6 min read
Moving files around is a core part of many Python scripts, whether you’re organizing downloads, deploying builds, or processing datasets. While most developers know the Unix mv
command, doing it in Python gives you more control, error handling, and cross-platform support. But have you ever wondered which built-in method is best when you need to move files across disks, preserve metadata, or log your actions?
In this guide, we'll answer that question by exploring the most common ways to move files in Python—os.rename
, shutil.move
, and the modern pathlib
API. You’ll learn the pros and cons of each, see real code examples, and discover tips for creating rock-solid file operations. By the end, you’ll be able to handle simple renames, cross-device transfers, and batch moves with confidence.
Moving files manually or via shell commands works fine in one-off cases, but automation demands consistency and error handling. If you’re writing a data pipeline or a deployment script, you need to be sure a file was moved successfully before continuing.
Python gives you:
Tip: If you want to log moved files to JSON, check out our guide on writing JSON to file in Python.
By moving files programmatically, you prevent surprises like overwriting important data or leaving temporary files behind.
The simplest way to move a file is using the os.rename
function. Under the hood, it’s a direct system call to rename or move a file.
import ossrc = 'old_folder/example.txt'dest = 'new_folder/example.txt'try:os.rename(src, dest)print(f'Moved {src} to {dest}')except FileNotFoundError:print(f'File not found: {src}')except PermissionError:print(f'Permission denied when moving: {src}')except OSError as e:print(f'Error moving file: {e}')
Key points:
OSError: Invalid cross-device link
if src
and dest
are on different mounts.Use os.rename
when you know both paths share the same file system and you need speed.
To move files between different disks or partitions, use shutil.move
. It will fall back to copy-and-delete when necessary.
import shutilsrc = '/mnt/usb/report.csv'dest = '/home/user/data/report.csv'try:new_path = shutil.move(src, dest)print(f'Moved to {new_path}')except Exception as e:print(f'Failed to move: {e}')
Advantages:
Disadvantages:
If you need reliability across file systems, shutil.move
is the most straightforward solution.
Python’s pathlib
module offers an object-oriented approach to filesystem paths. Since Python 3.4, it’s been a robust alternative to os
and shutil
.
from pathlib import Pathsrc = Path('downloads/data.json')dest_dir = Path('archive')dest_dir.mkdir(parents=True, exist_ok=True)dest = dest_dir / src.nametry:src.replace(dest)print(f'Replaced {src} to {dest}')except Exception as e:print(f'Error: {e}')
Notes:
replace()
acts like rename()
but overwrites existing files.dest_dir.joinpath(src.name)
or use the /
operator.replace
behaves like os.rename
, so fallback to manual copy if needed:import shutildef move_path(src: Path, dest: Path):try:src.replace(dest)except OSError:shutil.copy2(src, dest)src.unlink()
Using pathlib
makes your code cleaner and more readable.
File operations can fail in many ways. Here are some best practices to make your move scripts bulletproof:
Check existence before moving:
if not src.exists():raise FileNotFoundError(f'{src} not found')
Create destination dirs:
dest.parent.mkdir(parents=True, exist_ok=True)
Tip: Always test your script on a small set of files before running it on a large directory.
Method | Cross-Device | Preserves Metadata | Overwrites | Speed |
---|---|---|---|---|
os.rename | No | Yes | No | Fast |
shutil.move | Yes | Yes | No* | Medium |
pathlib.replace | No | Yes | Yes | Fast |
*shutil.move
may overwrite if dest exists and is a file.
Use this table to pick the right tool for your use case.
Imagine a script that moves all .log
files older than 7 days from /var/logs
to an archive folder.
import shutilimport timefrom pathlib import PathLOG_DIR = Path('/var/logs')ARCHIVE_DIR = Path('/var/archive/logs')ARCHIVE_DIR.mkdir(parents=True, exist_ok=True)cutoff = time.time() - 7 * 24 * 3600for file in LOG_DIR.glob('*.log'):if file.stat().st_mtime < cutoff:dest = ARCHIVE_DIR / file.nametry:shutil.move(str(file), str(dest))print(f'Archived {file.name}')except Exception as e:print(f'Failed to archive {file.name}: {e}')
This script demonstrates:
glob
.stat().st_mtime
.With a few dozen lines, you can automate cleanup tasks across servers or data pipelines.
Moving files in Python is more than a simple rename. You need to consider cross-device constraints, error handling, and maintainable code. Use os.rename
for fast, same-filesystem moves; switch to shutil.move
when you might cross partitions; and leverage pathlib
for clean, object-oriented paths. Wrap actions in try/except
, create missing directories on the fly, and log your operations for auditing or debugging.
By mastering these methods and following best practices, you’ll write scripts that are robust, cross-platform, and easy to maintain. So the next time you reach for the shell’s mv
, remember that Python gives you all the power you need—right within your script.