Linux mv Command: The Mechanics of Moving and Renaming Files#

2026-05-12 20:17

You use mv every day, but it’s more nuanced than you think. Moving and renaming files are the same operation in Linux — which sounds counterintuitive, but once you grasp this, everything else falls into place.

The Core: Inode Operations#

mv doesn’t actually “transport” data. It operates on directory entries (dentries).

# Rename = dentry modification within the same filesystem
mv old_name.txt new_name.txt

# Move across filesystems = actual data copy
mv /home/user/file.txt /mnt/usb/

Within the same filesystem, mv only modifies the directory entry pointer. The inode number stays the same, data blocks don’t move — it’s instantaneous. Across filesystems, mv performs cp + rm: copy the data first, then delete the source.

You can verify this with strace:

# Same filesystem: only a rename syscall
strace mv file1.txt file2.txt 2>&1 | grep -E "rename|link"
# rename("file1.txt", "file2.txt") = 0

# Cross-device: copy_file_range + unlink
strace mv /home/user/file.txt /mnt/usb/ 2>&1 | grep -E "copy|unlink"
# copy_file_range(3, ...) = 8192
# unlink("/home/user/file.txt") = 0

Common Patterns and Pitfalls#

1. Batch Renaming#

mv itself doesn’t support batch operations, but a for loop does the job:

# Rename all .txt files to .md
for f in *.txt; do
    mv "$f" "${f%.txt}.md"
done

If filenames contain spaces, omitting quotes will break things. ${f%.txt} is bash parameter expansion — it strips the shortest .txt match from the end.

For complex batch renames, use the rename command:

# Perl rename with regex support
rename 's/\.txt$/.md/' *.txt

2. Moving Directories#

mv doesn’t need -r for directories, unlike cp:

# Directories move directly, no recursive flag needed
mv mydir/ /target/path/

# cp requires -r
cp -r mydir/ /target/path/

Within the same filesystem, moving a directory only modifies the parent directory’s dentry — no recursive copying involved.

3. Overwrite Confirmation#

By default, mv silently overwrites the target file. Dangerous:

# Interactive mode: confirm before overwriting
mv -i source.txt target.txt

# No-clobber mode: skip if target exists
mv -n source.txt target.txt

# Backup mode: create backup before overwriting
mv -b source.txt target.txt
# Creates target.txt~ backup file

Add this to your .bashrc:

alias mv='mv -i'

4. Update Mode#

Only move when the source is newer than the target:

mv -u newer.log /var/log/

Great for log synchronization — avoids overwriting fresher data.

Edge Cases#

Target: Directory vs File#

mkdir backup
mv file.txt backup/    # file.txt moves into backup directory

# But if backup is a file...
mv file.txt backup     # backup file gets overwritten!

Add a trailing / to clarify your intent:

mv file.txt backup/    # Clearly expects backup to be a directory; errors if not

Target Already Exists as Directory#

mkdir target
mv src/ target/
# Result: target/src/, NOT replacing target/

mv places the source directory inside the target. To replace, you need rm -rf target/; mv src/ target/.

Permission Nuances#

mv requires write permission on the source directory (to remove the old dentry) and target directory (to create the new dentry), but NOT on the file itself:

# You can mv a read-only file
chmod 444 readonly.txt
mv readonly.txt new_name.txt  # Succeeds!

Many people think a read-only file can’t be moved. But moving modifies the directory, not the file.

Under the Hood: The rename() Syscall#

Linux’s rename() syscall is atomic — it either succeeds completely or fails completely, no intermediate state:

#include <stdio.h>
#include <errno.h>

int main() {
    if (rename("old.txt", "new.txt") != 0) {
        perror("rename failed");
        // EXDEV: cross-device, need manual copy + unlink
        if (errno == EXDEV) {
            printf("Cross-device link, need manual copy\n");
        }
        return 1;
    }
    printf("Renamed successfully\n");
    return 0;
}

The EXDEV error code (errno 18) signals a cross-filesystem operation. The mv command detects this and automatically switches to copy + unlink mode.

Atomicity has a practical benefit: in concurrent scenarios, using mv to replace config files is safe:

# Atomic config file update
mv config.json.tmp config.json
# Other processes read either the old or new file, never a partial one

This is the foundation of many hot-reload configurations.

Summary#

  • Within the same filesystem, mv modifies dentries — instant
  • Across filesystems, mv is effectively cp + rm
  • mv checks directory permissions, not file permissions
  • rename() is atomic — perfect for concurrent-safe file replacement
  • Always use mv -i to prevent accidental overwrites

Want to try mv variations online? Check out the Linux mv Command Guide.


Related tools: Linux cp File Copy | Linux ls Directory Listing