Mastering File Movement in Linux: Beyond Basic mv
Command Usage
A misused mv
can wipe out days of work, especially in a production shell session. File movement in Linux appears trivial, but an engineer’s workflow demands awareness—filesystem semantics, overwriting behavior, and integration with automation. Below: practical movement techniques, known pitfalls, real-world automation, and one non-obvious tactic that can save hours in postmortems.
The Core Mechanic
Moving a file within the same filesystem is a metadata operation. Example:
mv /srv/logs/app.log /srv/logs/archive/
Instantaneous; no data copied.
Rename while moving:
mv /srv/logs/app.log /srv/logs/archive/app_20240612.log
Renaming is a move; no distinction for the kernel if both paths live on the same mount point (stat -f /src
and stat -f /dest
report identical device numbers).
Risk: Silent Overwrites
Default mv
overwrites the target with zero warning:
mv app.yaml /etc/myapp/config/
# No prompt. Destination "app.yaml" is replaced if it exists.
Mitigation: Add -i
(interactive):
mv -i app.yaml /etc/myapp/config/
Shell confirms before clobbering:
mv: overwrite '/etc/myapp/config/app.yaml'?
Known issue: In batch scripts, interactive mode (-i) can stall the process. In CI/CD or cron jobs, consider -n
(no clobber), e.g., mv -n ...
, to avoid unexpected prompts or overwrites.
Bulk Moves and Shell Globbing
Deployments or mass archiving need pattern moves:
mv /var/backups/*.tar.gz /mnt/backupstore/
Quotes prevent globbing errors if filenames are ill-formed:
mv -- *.tar.gz /mnt/backupstore/
For special characters (spaces, newlines), batch script with null delimiters:
find . -name "*.mp4" -print0 | xargs -0 -I{} mv "{}" ~/media/
Gotcha: Always test with ls
first to ensure patterns are correct. Wildcards can easily overshoot, especially in recursive operations.
Directory Relocation
Moving entire directories remains atomic if source and target share a filesystem:
mv /var/log/old/ /archive/logs/2023-12/
If the target is another mounted device (check with df /path
):
mv /mnt/data/app_2023 /backup/app_2023
Linux falls back to copy+delete—slower, with the risk of partial data on disk full/error. For multi-gigabyte directories or critical logs, prefer rsync
with summary flags:
rsync -a --remove-source-files /mnt/data/app_2023/ /backup/app_2023/
rm -rf /mnt/data/app_2023
Pragmatic tip: --remove-source-files
only deletes files, not directories. Always clean up directory tree afterwards.
Real Automation: Archiving Old Logs
Example: Move .log
files older than 7 days to cold storage.
#!/bin/bash
set -euo pipefail
ARCHIVE=~/logs/archive-$(date +%Y%m%d)
mkdir -p "$ARCHIVE"
find ~/logs/ -type f -name "*.log" -mtime +7 -exec mv -i {} "$ARCHIVE"/ \;
Explanation:
- Uses
find
with-mtime +7
for safety. set -euo pipefail
adds error discipline—a must for production cron scripts.- All moves interactive. Batch renaming avoids log overwrite if duplicate basenames exist.
- Imperfection: If two logs with identical names come from different days, manual intervention is needed.
Visibility & Debugging
Add -v
for auditing:
mv -v *.csv /tmp/batch/
Terminal output:
renamed 'report1.csv' -> '/tmp/batch/report1.csv'
renamed 'report2.csv' -> '/tmp/batch/report2.csv'
Useful for test runs before invoking inside systemd services.
Side Note: Preserving Timestamps and Attributes
While mv
is atomic, it does not modify timestamps unless a cross-device move occurs. If you need fine-grained control (e.g., preserving original creation and modification times when combining copy+remove), combine cp -a
then rm
or use rsync -a
as above.
Chaining with Conditionals
Professional scripts should always check for file existence before move:
[ -e "docker-compose.yml" ] && mv -v "docker-compose.yml" infra/ || echo "No compose file found"
Rhetorical: What happens if you write a blind move in a pipeline with missing files? Failure modes can be subtle.
Non-obvious: Using --backup
for Versioning
Not widely known: mv
supports --backup
to preserve existing destination files:
mv --backup=numbered config.yaml /etc/app/
If /etc/app/config.yaml
exists, it's rotated: config.yaml~
, config.yaml.~1~
, etc.
Downside: Backups accumulate and must be managed.
Summary Table
Use Case | Command |
---|---|
Move, no overwrite | mv -n src dest/ |
Move, prompt on overwrite | mv -i src dest/ |
Bulk move, log output | mv -v *.log /archive/ |
Preserve backups on target | mv --backup=numbered src dest/ |
Cross-filesystem, keep attrs, safe delete | rsync -a --remove-source-files src/ dest/; rm -rf src/ |
Scripted move older than N days | Use find ... -mtime +N -exec mv ... |
Bottom line: The default mv
could lead to silent data loss, especially in scripts or automation. Harden your move logic: interactive/backup flags, checks for cross-device moves, and audit outputs. Not perfect, but these practical approaches reduce risk and support recovery—especially when something eventually goes sideways.