How to Move Files and Directories in Linux with mv
mv moves and renames files and directories. There is no separate rename command for a single file: renaming is just a move within the same directory.
Basic syntax
mv [options] SOURCE DEST
mv [options] SOURCE... DIRECTORY
mv report.txt ~/Documents/ # move into a directory
mv report.txt ~/Documents/q3.txt # move and rename
mv draft.md final.md # rename in place
mv /tmp/build.log /var/log/my-app/ # absolute paths
A trailing slash on the destination is a good habit. mv report.txt archive/ fails with an error if archive does not exist. Without the slash, mv report.txt archive would silently rename the file to archive.
Move files to another directory
With more than two arguments, the last one must be an existing directory:
mv a.txt b.txt c.txt backup/
mv *.log logs/
mv ~/Downloads/*.pdf ~/Documents/invoices/
The * glob does not match hidden files (names starting with a dot). To include them in bash:
shopt -s dotglob
mv src/* dest/
shopt -u dotglob
To move files selected by find, use -t, which puts the target directory first. That is the form -exec ... {} + and xargs need:
find . -maxdepth 1 -name '*.csv' -mtime +30 -exec mv -t archive/ {} +
Move or rename a directory
Directories move the same way. Unlike cp, mv needs no -r.
mv my-app /opt/
mv old-name new-name
The result depends on whether the destination already exists:
mv project backupwhenbackupdoes not exist renamesprojecttobackup.- When
backupis an existing directory,projectmoves inside it and becomesbackup/project. - If
backup/projectalready exists and is not empty,mvfails withDirectory not empty. It never merges two directories. Usersyncfor that (see below).
-T treats the destination as a plain name, so mv -T project backup either renames or fails, and never nests one directory inside the other.
Avoid overwriting files
By default mv silently replaces an existing file at the destination. These options make it safer:
mv -i report.txt archive/ # ask before overwriting
mv -n report.txt archive/ # never overwrite
mv -b report.txt archive/ # keep the old file as report.txt~
mv --backup=numbered report.txt archive/ # report.txt.~1~, report.txt.~2~, ...
mv -u *.txt archive/ # only when the source is newer
mv -v *.txt archive/ # print every move
If you combine -i, -n and -f, the last one wins. RHEL-family systems alias mv to mv -i for root, which is why it prompts there and not on Ubuntu. command mv bypasses the alias.
File names that start with a dash look like options. Put -- before them or prefix the path:
mv -- -old-name.txt new-name.txt
mv ./-old-name.txt new-name.txt
Rename many files at once
mv renames one file per call, so use a loop:
for f in *.txt; do mv -- "$f" "${f%.txt}.md"; done
${f%.txt} strips the .txt suffix from the name. The rename tool does the same in one command, but there are two different versions:
# Debian, Ubuntu: Perl rename (sudo apt install rename), -n is a dry run
rename -n 's/\.txt$/.md/' *.txt
# RHEL family: util-linux rename, takes "from to files"
rename .txt .md *.txt
Moving across filesystems
Within one filesystem, mv is a rename. It finishes instantly whatever the size, because no data moves, and ownership and timestamps stay as they were.
When source and destination are on different filesystems (another disk, partition, NFS mount, or /tmp on tmpfs), mv copies the data and then deletes the source. That takes time and needs free space at the destination. mv removes each source only after it has been copied, so an interrupted move leaves the original in place and possibly a partial copy at the destination. As a regular user, it also cannot keep the ownership of files that belong to other users.
For large trees, rsync is the better tool. It shows progress, can resume, and merges into an existing directory:
rsync -aHAX --info=progress2 /data/old/ /mnt/new/
rsync -aHAX --dry-run --itemize-changes /data/old/ /mnt/new/ # verify: should list nothing
rm -rf /data/old
-a keeps permissions, times and symlinks, -H keeps hard links, -A and -X keep ACLs and extended attributes. The trailing slash on the source matters: /data/old/ copies the contents, /data/old creates old inside the destination.
To move and delete in one pass:
rsync -a --remove-source-files /data/old/ /mnt/new/
find /data/old -type d -empty -delete
--remove-source-files deletes files but leaves the empty directory tree behind, which the find line cleans up.
To move a file to another machine, copy it over SSH and remove the local copy once the transfer succeeded:
rsync -a --remove-source-files backup.tar.gz user@203.0.113.10:/srv/backups/
Permissions and SELinux
Moving a file needs write and execute permission on both the source and the destination directory, not on the file itself. In directories with the sticky bit, like /tmp, you can only move out files you own. For system paths use sudo:
sudo mv my-app.service /etc/systemd/system/
On RHEL, Fedora and other SELinux systems, mv keeps the file's original security context. A unit file or web content moved from your home directory keeps a home-directory label, and the service then gets "Permission denied" even though the mode looks right. Reset the label after the move, or copy instead of moving:
sudo restorecon -v /etc/systemd/system/my-app.service
Quick reference
mv file.txt dir/ # move into a directory
mv old.txt new.txt # rename
mv a b c dir/ # several files
mv -t dir/ file1 file2 # target first (for find/xargs)
mv -i file.txt dir/ # ask before overwriting
mv -n file.txt dir/ # never overwrite
mv --backup=numbered file.txt dir/ # keep old versions
mv -v src/* dest/ # verbose
mv -T src-dir dest-dir # rename, never nest
for f in *.txt; do mv -- "$f" "${f%.txt}.md"; done
rsync -aHAX --info=progress2 src/ dest/ # big or cross-disk moves
