Move Files Safely with GNU mv

mv looks trivial until it silently overwrites a file, because it never asks first by default. This guide builds a repeatable way to rename files, move several into a directory, and stop an important destination from vanishing by accident. The examples use GNU mv from coreutils 9.4, package version 9.4-3ubuntu6.3.

Allow about ten minutes. You need a shell and two paths that you can safely change. This guide uses ordinary user permissions. Use elevated privileges only when the source or destination is not writable by your account; sudo mv does not make a risky destination safe.

1. Check the installed command

Confirm which executable your shell will run and record its version. These are read-only checks:

$ command -v mv
/usr/bin/mv
$ mv --version
mv (GNU coreutils) 9.4

The command has two main forms. mv SOURCE DEST renames one source or moves it to a destination path. mv SOURCE... DIRECTORY moves every listed source into an existing directory. A destination that already names a directory is treated as a directory, which is a common source of surprises when a path is mistyped.

Checkpoint: write down the exact source and destination paths before changing either. Use ls -ld -- PATH to inspect a path without trusting your assumptions:

$ ls -ld -- /path/to/source /path/to/destination

2. Rename one file and verify it

Renaming within one directory changes the directory entry. The file contents are not rewritten in the normal case:

$ mv --verbose -- /path/to/report.txt /path/to/report-final.txt
renamed '/path/to/report.txt' -> '/path/to/report-final.txt'
$ test -f /path/to/report-final.txt && echo 'new name exists'
new name exists
$ test ! -e /path/to/report.txt && echo 'old name is gone'
old name is gone

--verbose prints what was done. The -- marks the end of options, so a path beginning with a hyphen is treated as a path rather than another option. Prefer an absolute path or one beginning with ./ when handling names supplied by another person or program.

Undo this example by moving the renamed file back, provided the old name has not been reused:

$ mv -- /path/to/report-final.txt /path/to/report.txt

3. Move several files into an existing directory

For several sources, the final argument must be a directory. Create it first if that is part of your intended change, then inspect it before moving anything:

$ mkdir -p -- /path/to/archive
$ ls -ld -- /path/to/archive
drwxr-xr-x 2 user user 4096 ... /path/to/archive
$ mv --verbose -- /path/to/report-final.txt /path/to/notes.txt /path/to/archive/
renamed '/path/to/report-final.txt' -> '/path/to/archive/report-final.txt'
renamed '/path/to/notes.txt' -> '/path/to/archive/notes.txt'
$ ls -l -- /path/to/archive/

This operation changes the locations of the sources. To undo it, move each file back to its original path while the original parent directory still exists. If a destination file already exists, the default operation can replace it, which is why a plain multi-file command should not be your first move in an unfamiliar directory.

4. Prevent an existing destination being replaced

Warning: Without a protection option, GNU mv can replace an existing destination. Use --no-clobber when preserving the destination matters:

$ mv --no-clobber --verbose -- /path/to/incoming.txt /path/to/current.txt
mv: not replacing '/path/to/current.txt'
$ printf 'exit status: %s\n' "$?"
exit status: 1
$ test -f /path/to/incoming.txt && echo 'source remains'
source remains

A skipped file is still a decision worth checking. The command returns a non-zero status in this case, so a script must handle it rather than assume every source moved. If you deliberately want an overwrite prompt instead, use --interactive: it asks before replacing a destination, so answer only after checking both paths.

Do not combine --no-clobber, --interactive and --force casually. If more than one is supplied, only the final one takes effect, so the order in the command line changes the safety behaviour.

5. Update only older destination files

For synchronisation-style work, --update=older replaces an existing destination only when it is older than the corresponding source. It is the default mode selected by --update:

$ mv --update=older --verbose -- /path/to/newer-report.txt /path/to/report.txt
$ printf 'exit status: %s\n' "$?"
exit status: 0

With --update=older, a destination that is the same age or newer is left in place. The command may print no verbose line for a skipped source, so verify the timestamps and contents if the result matters:

$ stat --format='source: %y' /path/to/newer-report.txt
source: 2026-09-25 10:15:00.000000000 +0000
$ stat --format='destination: %y' /path/to/report.txt
destination: 2026-09-25 10:10:00.000000000 +0000

The other explicit modes are --update=all, which replaces existing destinations, and --update=none, which skips existing destinations without treating the skip as a failure. Do not use timestamps as a substitute for content comparison when clock accuracy or file contents are uncertain.

6. Keep a backup when replacement is intentional

Warning: A move is a state change, and a replacement can make recovery harder. When you need the new file at the destination but want the old one retained, use --backup:

$ mv --backup --verbose -- /path/to/revised.txt /path/to/report.txt
renamed '/path/to/revised.txt' -> '/path/to/report.txt' (backup: '/path/to/report.txt~')
$ ls -l -- /path/to/report.txt /path/to/report.txt~

The normal backup suffix is ~. Set a different suffix with --suffix=.bak, or choose numbered backups with --backup=numbered. The backup is another file that must be protected and eventually reviewed. Restore it only after checking that the destination is the file you intend to replace:

$ mv --interactive -- /path/to/report.txt~ /path/to/report.txt
mv: overwrite '/path/to/report.txt'? y

Do not remove the backup as part of an unreviewed batch. Deleting it is irreversible unless another copy exists.

7. Diagnose a failed move

First check whether the source still exists and whether the destination is a directory:

$ ls -ld -- /path/to/source /path/to/destination
$ test -r /path/to/source && echo 'source is readable'
$ test -d /path/to/destination && echo 'destination is a directory'

A missing source, an unwritable parent directory, or a destination that is unexpectedly a directory can each change the result. Read the complete diagnostic before retrying. A move between file systems may be implemented as a copy followed by removal, so a failure after copying can leave both paths or a partial destination behind. Check both paths and compare contents before taking another action.

For a dry inspection of the paths, use ls and stat; there is no general dry-run mode in the installed mv. --debug implies --verbose and explains how a file is copied, which can help when a rename is not possible:

$ mv --debug --no-clobber -- /path/to/source /path/to/destination

The exact diagnostic depends on the paths, permissions and file systems. Treat a non-zero status as unfinished work until you have inspected the source and destination.

Done means