Mastering the mv
Command: Practical File Movement on Linux
Misusing mv
risks silent data loss. Yet, executed with rigor, it accelerates workflow, enforces data hygiene, and becomes essential in both routine scripts and production CI pipelines.
Fundamentals
mv
handles two primary tasks: transferring a file or directory to a new location, and/or renaming it. On ext4 (common in Ubuntu 20.04+), a same-filesystem move is an inode update—nearly instantaneous, zero-copy. But there are subtleties worth noting.
Basic usage:
mv [OPTIONS] SOURCE TARGET
SOURCE
: file(s) or directory to move.TARGET
: destination directory or filename.
Quick example:
Move logs.txt
to /var/archive/
(overwrites silently if same-named file exists):
mv logs.txt /var/archive/
Rename while moving:
mv logs.txt /var/archive/logs-2024-06-14.txt
Overwrite Protection
The default behavior—overwriting without prompting—has caused unnecessary late-night recoveries. mv -i
prompts for confirmation:
mv -i *.conf /etc/myapp/
# mv: overwrite '/etc/myapp/app.conf'? y/n
For unattended scripts where "do not overwrite" is mandatory, prefer -n
:
mv -n report.csv /mnt/backup/
# If /mnt/backup/report.csv exists, nothing is done—no prompt.
Gotcha: Both -i
and -n
together: -n
takes precedence.
Visibility: Watching What Happens
mv -v
(verbose) displays every operation:
mv -v *.log /tmp/logs/
# 'error.log' -> '/tmp/logs/error.log'
# 'access.log' -> '/tmp/logs/access.log'
This is indispensable in troubleshooting automation, or where logs are required for auditing.
Moving Multiple Items
Wildcards (*
, ?
) expand files—subject to shell glob rules:
mv *.tar.gz /srv/tarballs/
Or explicitly list:
mv image1.png image2.png assets/
Bulk moves in scripts should always check for existence or permissions (see next section).
Moving Directories
mv
works recursively for directories by default. But, unexpectedly, issues arise across filesystems:
mv data/ /mnt/export/
Running strace
on this command across filesystems (use df .
to verify) reveals mv
does a copy+delete; it’s not atomic and is interrupted by a sudden SIGKILL
(see partial directories—ls
shows moved files, some linger in source).
For large datasets, consider rsync
with --remove-source-files
and proper verification.
Cross-Device Moves
Expect the following difference:
- Same device: inode move, fast, atomic.
- Between
/
and/mnt/usb/
: actual file copy occurs.
Manual alternative (showing “interleaving” danger):
cp --preserve=all bigfile.img /mnt/usb/
# Only after verifying size/checksum:
rm bigfile.img
Note: Ejecting storage before completion leaves partial files and no warning. For critical data, hash with sha256sum
before removing the original.
Scripting and Automation
Automated file archival is a standard pattern in centralized logging and retention management.
Example: Archive .log
files older than 30 days:
#!/bin/bash
find /var/log/myapp/ -type f -name '*.log' -mtime +30 -exec mv -v {} /var/log/myapp/archive/ \;
-mtime +30
: Files last modified >30 days ago.-exec mv -v {}
: Atomically moves in a loop; possible race if file is removed mid-process.
Known Issue: With high file churn, there's a window where find
returns a file that disappears by move-time (mv: cannot stat ...
).
Wrap in if [ -e "$file" ]; then mv ...; fi
for more robust error handling.
Advanced Insight
Permissions
Moving files requires write permissions on the source’s parent (so inode can be unlinked) and on the target directory. When scripts fail silently, usually it’s a permissions issue:
mv /data/secure/db.sqlite /backup/
# mv: cannot move 'db.sqlite' to '/backup/db.sqlite': Permission denied
Testing With echo
For destructive batch moves, echo the command first:
find . -name '*.bak' -mtime +14 -exec echo mv {} /mnt/backups/old/ \;
Scrutinize output, then run without echo
.
File Patterns and Hidden Files
By default, mv * target/
does not match dotfiles (files beginning with .
). Use shopt -s dotglob
in Bash if hidden files must be included.
Quick Reference Table
Scenario | Command Example |
---|---|
Move single file | mv report.md /srv/archive/ |
Rename while moving | mv test.sh /scripts/test-2024-06.sh |
Move multiple files | mv *.dat rawdata/ |
Prompt before overwrite | mv -i server.cfg /etc/app/ |
Never overwrite | mv -n data.json /backup/ |
Verbose (track every move) | mv -v img1.png img2.png media/ |
Move directory | mv old_data/ processed/ |
Move across filesystems (manual) | cp --preserve=all large.mkv /mnt/usb/ && rm large.mkv |
Underrated Tactics
- Use
mv --backup=numbered
(mv -n --backup=numbered file dest/
) for automatic versioning if moved file already exists. - Consider locking mechanisms (
flock
) in concurrent scripts to avoid race conditions. - Incorporate
set -euo pipefail
atop shell scripts for robust error management during batch moves.
In summary:
mv
is deceptively simple, yet unforgiving—a single misstep can erase data without a trace. Test with non-critical files, log every move when automating, and be hyper-aware of filesystem boundaries and permissions. Sometimes, for non-atomic moves or high-load environments, rsync
or a transactional copy/delete is the safer trade-off.
Note: For further detail, consult man mv
(coreutils >=8.30), or observe modifications via inotifywait
for real-time move tracking during scripting/debugging.