Question
How can I perform the equivalent of the Unix mv command in Python to move a file from one location to another?
path/to/current/file.foo
path/to/new/destination/for/file.foo
Short Answer
Python's shutil.move() function is the usual equivalent of mv. It can move a file into another directory or move and rename it by supplying a new file path. You will learn how to use it safely, how destination paths affect the result, and when os.rename() may be appropriate instead.
Concept
The standard-library shutil module provides high-level file operations. Its shutil.move(source, destination) function moves a file or directory.
import shutil
shutil.move("path/to/current/file.foo", "path/to/new/destination/file.foo")
Moving means the item no longer exists at the source location after a successful operation. Depending on the filesystems involved, Python may rename the item directly or copy it to the destination and then remove the original.
The destination argument has two useful forms:
- An existing directory: the source keeps its original name inside that directory.
- A new file path: the source is moved and given the specified new name.
This matters in real programs that organize uploads, archive reports, process incoming files, or rename files after they have been generated.
Mental Model
Think of a file as a labelled folder in a filing cabinet.
- Moving it into a different drawer keeps the label the same.
- Moving it into a different drawer and attaching a new label both relocates and renames it.
shutil.move() is the person who performs that filing task. You tell it where the folder is now and where you want it to end up.
Syntax and Examples
The basic syntax is:
import shutil
shutil.move(source, destination)
Move a file and keep its name
If archive/ already exists, this puts report.csv inside it:
import shutil
shutil.move("reports/report.csv", "archive/")
The resulting path is:
archive/report.csv
Move and rename a file
Give a complete destination file path to change the name while moving:
import shutil
shutil.move(
"reports/report.csv",
"archive/report-2025.csv"
)
Use pathlib.Path for readable paths
pathlib is often easier to read than long string paths:
from pathlib import Path
import shutil
source = Path("downloads") /
destination = Path() /
destination.parent.mkdir(parents=, exist_ok=)
shutil.move(source, destination)
Step by Step Execution
Consider this code:
from pathlib import Path
import shutil
source = Path("inbox") / "invoice.pdf"
destination = Path("processed") / "invoice.pdf"
destination.parent.mkdir(exist_ok=True)
shutil.move(source, destination)
Step by step:
sourcerepresentsinbox/invoice.pdf.destinationrepresentsprocessed/invoice.pdf.destination.parentisprocessed.mkdir(exist_ok=True)createsprocessedif it is absent. If it already exists, no error is raised.shutil.move(source, destination)relocates the file.- After success,
processed/invoice.pdfexists andinbox/invoice.pdfno longer exists.
If the source file is missing or the program lacks permission, shutil.move() raises an exception.
Real World Use Cases
- Upload processing: Move a newly uploaded file from a temporary directory into permanent storage.
- Report archiving: Move a generated CSV or PDF into a dated archive folder.
- Media organization: Move photos from a download folder into categorized folders.
- Batch processing: Move successfully processed files to
processed/and failed files tofailed/. - Log rotation scripts: Move old log files into an archive before creating fresh logs.
- Data pipelines: Move input files after they have been imported, so they are not processed twice.
Real Codebase Usage
In production code, file moves are usually surrounded by validation and error handling.
Validate before moving
from pathlib import Path
import shutil
source = Path("incoming/orders.json")
destination_dir = Path("processed")
if not source.is_file():
raise FileNotFoundError(f"Expected file was not found: {source}")
destination_dir.mkdir(parents=True, exist_ok=True)
shutil.move(source, destination_dir)
Use a guard clause for duplicate handling
A destination file may already exist. Decide whether your application should reject it, rename the incoming file, or intentionally replace it.
from pathlib import Path
import shutil
source = Path("incoming/data.csv")
destination = Path("archive/data.csv")
if destination.exists():
raise FileExistsError(f"Refusing to replace existing file: {destination}")
destination.parent.mkdir(parents=True, exist_ok=True)
shutil.move(source, destination)
Handle expected operating-system errors
shutil
:
shutil.move(, )
FileNotFoundError:
()
PermissionError:
()
OSError error:
()
Common Mistakes
Assuming destination directories are created automatically
This fails if archive/2025/ does not exist:
import shutil
shutil.move("report.csv", "archive/2025/report.csv")
Create the parent directory first:
from pathlib import Path
import shutil
new_path = Path("archive/2025/report.csv")
new_path.parent.mkdir(parents=True, exist_ok=True)
shutil.move("report.csv", new_path)
Confusing a directory destination with a file destination
shutil.move("report.csv", "archive/")
If archive/ exists, the result is archive/report.csv, not a file literally named archive.
Forgetting that a move removes the original
After a successful move, do not continue to read or modify the old path. Store and use the returned destination path if needed:
import shutil
new_location = shutil.move(, )
(new_location)
Comparisons
| Tool | Best use | Important behavior |
|---|---|---|
shutil.move(source, destination) | General-purpose moves and renames | Can work across filesystems by copying then removing the source. |
Path.rename(target) | Rename or move on the same filesystem | Usually maps directly to an OS rename operation; may fail across filesystems. |
os.rename(source, destination) | Low-level rename or move | Similar purpose to Path.rename() but uses string/path arguments. |
shutil.copy(source, destination) | Keep the original and create a duplicate | Copies file contents; source remains. |
Path.replace(target) | Intentionally replace a target path | Useful when replacement is explicitly desired; platform details still matter. |
Cheat Sheet
import shutil
from pathlib import Path
# Move into an existing directory; keeps original file name.
shutil.move("source/report.csv", "archive/")
# Move and rename.
shutil.move("source/report.csv", "archive/final-report.csv")
# Create missing parent directories first.
destination = Path("archive/2025/final-report.csv")
destination.parent.mkdir(parents=True, exist_ok=True)
shutil.move("source/report.csv", destination)
- Use
shutil.move(source, destination)for the Python equivalent ofmv. - If destination is an existing directory, the source name is retained.
- If destination is a file path, the item can be renamed.
- Check
source.is_file()orsource.exists()before moving when appropriate. - Create destination directories yourself with
mkdir(). - Check
destination.exists()before a move if overwriting would be dangerous. - Catch
FileNotFoundError,PermissionError, andOSErrorin scripts that must handle failures.
FAQ
What is the Python equivalent of mv?
Use shutil.move(source, destination) from Python's standard library.
Does shutil.move() rename files too?
Yes. Supply a destination path with a new filename, such as "archive/old-report.csv".
Does shutil.move() create the destination folder?
No. Create missing parent directories before calling it, for example with Path(...).parent.mkdir(parents=True, exist_ok=True).
What happens if the destination is a directory?
If the directory already exists, Python places the source inside it and keeps the source item's name.
Can Python move directories with shutil.move()?
Yes. shutil.move() works with both files and directories.
Is shutil.move() the same as copying a file?
No. A copy leaves the original in place. A successful move removes the item from its original location.
Should I use os.system("mv ...") in Python?
Usually no. shutil.move() is portable, avoids shell quoting problems, and accepts objects.
Mini Project
Description
Create a small file-organizing script for a downloads folder. It examines each file's extension and moves it into an appropriate category directory such as images, documents, or other. This demonstrates how repeated calls to shutil.move() can turn a manual filing task into a reliable script.
Goal
Organize files from an input folder into category folders while preserving each filename.
Requirements
Requirement 1
Keep learning
Related questions
@staticmethod vs @classmethod in Python Explained
Learn the difference between @staticmethod and @classmethod in Python with clear examples, use cases, mistakes, and a mini project.
Add Rows to a Pandas DataFrame in Python
Learn how to add rows to a Pandas DataFrame, why repeated row appends are slow, and when to use loc, concat, or record lists.
Call a Function by Name in a Python Module
Learn how to call a function by name in a Python module using strings, getattr, and safe patterns for dynamic function dispatch.