Short answer: to rename a directory in Linux, run mv old_name new_name. The same command renames a file, and mv file dir/ moves a file into a directory. One trap: if new_name is already an existing directory, mv puts old_name inside it instead of renaming it. Add -i to be asked before anything gets overwritten.
mv old_name new_name # rename a directory (or a file), no -r needed
mv report.pdf archive/ # move a file into a directory
mv -i draft.txt final.txt # ask before overwriting final.txt
mv -T site site-old # rename only; never move site into an existing site-oldThe outputs on this page come from Debian 12 with GNU coreutils 9.1 and bash 5.2.15, run in a scratch directory on 21 October 2026, including rename.ul from util-linux 2.38.1 and rsync 3.2.7. The Perl rename lines follow its man page, and the cross-filesystem behaviour follows the GNU coreutils manual; neither was run here. Ubuntu, Rocky Linux and AlmaLinux ship the same GNU mv.
mv cheat sheet
| Task | Command |
|---|---|
| Rename a directory | mv logs logs-2026 |
| Rename a file | mv notes.txt todo.txt |
| Move a file into a directory | mv report.pdf archive/ |
| Move several files into a directory | mv *.log archive/ |
| Target directory first (for find and xargs) | mv -t archive/ a.log b.log |
| Ask before overwriting | mv -i a.conf b.conf |
| Never overwrite | mv -n a.conf b.conf |
| Keep a backup of what gets overwritten | mv -b a.conf b.conf |
| Only if the source is newer | mv -u build/app.js public/app.js |
| Show what happens | mv -v old new |
| Rename, never move into an existing directory | mv -T site site-old |
| Name that starts with a dash | mv -- -draft draft |
| Name with spaces | mv "My Folder" my-folder |
How do I rename a directory in Linux?
Give mv the current name and the new one. Unlike cp, it needs no -r for a directory, because nothing is copied: on the same filesystem, mv only changes the name in the parent directory. That is why renaming a directory with thousands of files is instant, and why owner, permissions and timestamps stay exactly as they were.
$ mv -v old_name new_name
renamed 'old_name' -> 'new_name'
$ ls -R new_name
new_name:
sub
new_name/sub:
aPaths work too, so mv /srv/app/releases/v1 /srv/app/releases/v1-broken renames a directory without changing into its parent first. Quote names with spaces: mv My Folder new without quotes tries to move two things called My and Folder.
When the new name already exists as a directory
This is where renaming goes wrong. If the second name is an existing directory, mv does not replace it; it moves the first directory into it:
$ mkdir site site-old
$ mv -v site site-old
renamed 'site' -> 'site-old/site'Run the same command a day later in a deploy script, after site-old already exists, and you get a nested site-old/site instead of a rename. -T (--no-target-directory) tells mv to treat the second name as the new name and nothing else. It replaces an empty directory and refuses a full one:
$ mv -Tv site2 empty
renamed 'site2' -> 'empty'
$ mv -T src dst
mv: cannot move 'src' to 'dst': Directory not emptymv never merges two directories. To combine src into an existing dst, copy with rsync and remove the source afterwards, as shown further down.
Rename a file, and what happens to an existing one
mv notes.txt todo.txt renames a file. If todo.txt already exists, plain mv replaces it without a word, and the old content is gone. Three options protect it:
$ mv -i a.conf b.conf
mv: overwrite 'b.conf'? n
$ mv -nv a.conf b.conf # -n: silently skip, b.conf untouched
$ mv -bv a.conf b.conf # -b: overwrite, keep the old one as b.conf~
renamed 'a.conf' -> 'b.conf' (backup: 'b.conf~')--backup=numbered keeps every old version instead of only the last one (d.~1~, d.~2~, and so on), and -S .bak changes the suffix of a simple backup from ~ to .bak. On a server, mv -b is a cheap safety net when you put a new config file in place of a working one.
Move files into a directory
When the last argument is an existing directory, mv moves everything before it into that directory and keeps the names. Write the directory with a trailing slash: if it does not exist (a typo), mv then stops instead of renaming your file to the typo:
$ mv -v *.log archive/
renamed 'a.log' -> 'archive/a.log'
renamed 'b.log' -> 'archive/b.log'
renamed 'c.log' -> 'archive/c.log'
$ mv -v x.txt nodir/
mv: cannot move 'x.txt' to 'nodir/': Not a directoryWith more than two names, the last one must be a directory, otherwise mv answers target 'r': Not a directory. -t DIR names the target directory first, which is what find -exec ... + and xargs need, because they append the file names at the end. The find guide explains the + form:
# move logs older than 30 days, from anywhere below logs/, into old/
find logs -name '*.log' -mtime +30 -exec mv -v -t old/ {} +
# the same with xargs, safe for names with spaces
find logs -name '*.log' -mtime +30 -print0 | xargs -0 mv -t old/That is also the fix for Argument list too long, which bash prints when a glob such as mv *.jpg photos/ expands to more names than the kernel accepts in one command line.
mv options: -i, -n, -f, -u, -v
| Option | Long form | What it does |
|---|---|---|
-i | --interactive | Ask before overwriting an existing file |
-n | --no-clobber | Never overwrite; skip the file |
-f | --force | Never ask. Without it, mv still asks before replacing a write-protected file when run in a terminal |
-u | --update | Move only if the source is newer than the destination, or the destination is missing |
-v | --verbose | Print each rename |
-b | --backup | Keep the overwritten file as NAME~ |
-t DIR | --target-directory=DIR | Move everything into DIR, named first |
-T | --no-target-directory | Treat the last name as the new name, never as a directory to move into |
If you combine -i, -f and -n, the last one wins: mv -f -i asks, mv -i -f does not. That matters because Rocky Linux and AlmaLinux alias mv to mv -i in root’s ~/.bashrc (Debian ships the same line commented out in /usr/share/base-files/dot.bashrc), so mv -f typed at that prompt still overwrites without asking. Scripts never see the alias: bash does not expand aliases in a non-interactive shell.
Rename many files at once
mv renames one thing at a time. For a batch, either loop over the files in bash and build each new name with parameter expansion, or use a rename tool. The loop works on every system with bash and needs nothing installed:
# .jpeg to .jpg
for f in *.jpeg; do mv -v -- "$f" "${f%.jpeg}.jpg"; done
# spaces to underscores
for f in *' '*; do mv -v -- "$f" "${f// /_}"; done
# upper case to lower case (bash 4 and later)
for f in *.TXT; do mv -v -- "$f" "${f,,}"; done
# add a prefix
for f in IMG_*.jpg; do mv -v -- "$f" "2026-10_$f"; donerenamed 'IMG_001.jpeg' -> 'IMG_001.jpg'
renamed 'IMG_002.jpeg' -> 'IMG_002.jpg'
renamed 'holiday photo.jpeg' -> 'holiday photo.jpg'| Expansion | Example | Meaning |
|---|---|---|
${f%.jpeg} | photo.jpeg becomes photo | Remove a suffix |
${f#IMG_} | IMG_001.jpg becomes 001.jpg | Remove a prefix |
${f// /_} | my photo.jpg becomes my_photo.jpg | Replace every match |
${f,,} | README.TXT becomes readme.txt | Lower case (bash 4 and later) |
${f^^} | notes.txt becomes NOTES.TXT | Upper case (bash 4 and later) |
Three habits keep a loop safe. Run it once with echo mv in place of mv to see the commands first. Quote "$f" everywhere, so names with spaces survive. And put -- before the names, so a file called -draft is not read as an option. If no file matches, bash passes the pattern itself (*.png) to the loop; shopt -s nullglob turns that into an empty loop.
rename: two different tools with the same name
On Debian and Ubuntu, the rename package installs the Perl version, which takes a substitution expression. Rocky Linux, AlmaLinux and Fedora ship the util-linux version as rename, which replaces a plain string; Debian has that one as rename.ul. A command written for one fails on the other:
rename -n 's/\.jpeg$/.jpg/' *.jpeg # -n: show what would happen
rename 's/\.jpeg$/.jpg/' *.jpeg$ rename.ul -n -v .bak .log *.bak
`log1.bak' -> `log1.log'
`log2.bak' -> `log2.log'
$ rename.ul -v .old .bak *.old
`log1.old' -> `log1.bak'
`log2.old' -> `log2.bak'The util-linux version replaces the first occurrence of the string anywhere in the name, not only at the end, so check with -n first. For anything more than a suffix swap, the bash loop is easier to read and to review.
Moving across filesystems: copy, then delete
Inside one filesystem, mv calls the rename() system call, and the data never moves. Between two filesystems (another disk, a separate partition, an NFS mount, /tmp on tmpfs), rename is impossible, and per the GNU coreutils manual mv falls back to copying the files and then removing the originals. That changes what you should expect:
- It takes as long as a copy, and the target filesystem needs room for everything before the source is freed.
- An interrupted move leaves two halves: the source is removed only after the copy, so a Ctrl+C or a dropped SSH session leaves a partial copy at the destination. Run long moves inside tmux or screen.
- Checking which filesystem a path is on:
df -h /srv/data /mnt/backup; two different lines in the first column mean a copy, not a rename.
For a large move between disks or servers, rsync is the better tool, because it can resume where it stopped and only removes a source file once it is safely copied:
rsync -a --remove-source-files src/ dst/ # copy, deleting each file once it is copied
find src -type d -empty -delete # rsync leaves the empty folders behindIn the test, rsync moved src/a and src/sub/b into dst/ and left src and src/sub as empty folders, which the second line removed.
mv vs cp vs rsync
| mv | cp | rsync | |
|---|---|---|---|
| Source afterwards | Gone (renamed) | Still there | Still there, unless --remove-source-files |
| Same filesystem | Instant rename, no data copied | Full copy | Full copy, skips unchanged files on a rerun |
| Directory needs a flag | No | -r (or -a) | -a |
| Owner and timestamps | Kept (same file) | New owner and current time unless -a or -p | Kept with -a |
| Interrupted | Same fs: nothing to interrupt; across fs: partial copy | Partial copy | Rerun continues |
| Merges into an existing directory | No | Yes, with cp -a src/. dst/ | Yes |
| Over SSH to another server | No | No (scp does) | Yes |
The timestamp row is easy to check: a file moved with mv keeps its inode number and its modification time, while a plain cp of a file dated 1 January 2025 came out with the current time. For copies between servers, see transferring files to a VPS.
mv errors and what they mean
| Message ends with | Meaning | Fix |
|---|---|---|
| Directory not empty | The new name is a directory that already has content (with -T, or an existing dir of the same name inside the target) | Pick another name, or merge with rsync |
| Permission denied | No write and execute permission on the source or the target directory | See below |
| are the same file | Both names point to the same file: the same path, ./name, or two hard links | Nothing to do; check the paths |
| a subdirectory of itself | You tried mv dir dir/child | Move it somewhere outside itself |
| cannot overwrite directory with non-directory | A file would replace a directory | Rename or remove the directory first |
| Not a directory | The target ends with / but is not a directory, or several sources and the last name is no directory | mkdir -p |
| No such file or directory | The source does not exist (cannot stat), or the target's parent is missing | Check spelling with ls; create the parent |
| missing destination file operand | Only one name given | Add the new name |
| Argument list too long | A glob matched too many files | find ... -exec mv -t DIR {} + |
| Device or resource busy | Per rename(2): the path is a mount point, or in use as one | Unmount it, or rename inside it instead |
| Read-only file system | The filesystem is mounted read-only | Read-only file system |
mv: cannot move: Permission denied
mv: cannot move 'rootdir/file' to 'mine/file': Permission denied
mv: cannot move 'rootdir/file' to 'rootdir/file2': Permission deniedA rename changes two directories, not the file: it removes the name from the old parent and adds it to the new one. You need write and execute permission on both parents, and a file’s own mode does not matter. The second line shows it: even renaming a file inside the same root-owned folder failed. Moving a directory to another parent also needs write permission on that directory itself, because its .. entry changes (per rename(2)). Directories with the sticky bit, such as /tmp, add a rule on top: only the file’s owner, the directory’s owner or root may rename or move it out. namei -l /path/to/file lists owner and mode of every directory on the way, and the chmod guide explains what write and execute mean on a directory.
Renaming directories on a live server
On a server, a rename is rarely just a rename: something else usually has the old path written down.
- Configs keep the old path. Rename
/var/www/shopand nginx, PHP-FPM pools, systemd units and crontab lines that name it break at their next reload. Search before you rename:sudo grep -rl /var/www/shop /etc. - Running processes do not notice. A program keeps the files it has open and its working directory through a rename, so it may seem fine until it restarts and looks for the old path.
- Switch releases with a symlink, not a rename. Point a
currentlink at the live release and replace the link, so the web server never sees a half-finished directory.mv -Treplaces it in one step, becauserename()replaces the old name atomically:
ln -s releases/v2 current.new
mv -Tv current.new current
# renamed 'current.new' -> 'current'Without -T, mv follows the existing current link to its directory and drops current.new inside the old release, which is exactly what it did in the test (renamed 'current.new' -> 'current/current.new'). Create the folders you move things into with mkdir -p, and before a big reorganisation on a VMHeaven VPS, take a snapshot in the panel: it is the undo that mv itself does not have.
Frequently asked
How do I rename a directory in Linux?
Run mv old_name new_name in the directory that contains it, or use full paths: mv /srv/app/old /srv/app/new. mv needs no -r for a directory. If new_name already exists as a directory, mv moves old_name inside it instead of renaming it; mv -T old_name new_name only ever renames.
Why did mv put my folder inside the other folder instead of renaming it?
Because the new name already existed as a directory, and mv treats an existing directory as the place to move into. mv -T (--no-target-directory) treats the second name strictly as the new name: it replaces an empty directory and stops with Directory not empty if the target has content.
How do I rename many files at once in Linux?
Use a bash loop with parameter expansion, for example: for f in *.jpeg; do mv "$f" "${f%.jpeg}.jpg"; done. Run it with echo mv first to check the names. A rename tool also works, but there are two: the Perl rename on Debian and Ubuntu takes an expression (rename 's/\.jpeg$/.jpg/' *.jpeg), the util-linux rename on Rocky Linux and AlmaLinux takes plain strings (rename .jpeg .jpg *.jpeg).
Does mv work across different disks or partitions?
Yes, but then it is no longer a rename: mv copies the data to the other filesystem and removes the source afterwards. It takes as long as a copy and needs free space on the target. For large moves, rsync -a --remove-source-files is easier to resume after an interruption.