Linux: Moving Files Between Directories with Control and Metadata Preservation
Reorganizing data on production servers, sorting gigabytes of logs, or deploying release assets: on real Linux systems, moving files is rarely as trivial as mv src dest
. The classic mv
gets the job done on a single device, but specifics like permissions, ACLs, SELinux labels, and even cross-device boundaries quickly complicate matters.
Real-World Problem: Preventing Data Loss During File Relocation
Consider routine log rotation. A script that blindly moves /var/log/*.gz
into an archive can silently overwrite existing files, drop extended attributes, or, if crossing filesystem boundaries (/var
→ /mnt/backup
), disrupt inode-level guarantees. In CI runners, you might inadvertently lose symlinks or misattribute ownership—root cause for subtle bugs months later.
Here’s how experienced engineers keep relocation operations predictable and safe.
The Limitations of Basic mv
mv /path/to/file.log /archive/directory/
Straightforward, but with constraints:
- Overwrites target files without warning unless
-i
or-n
is given. - No metadata preservation beyond default stat bits; extended attributes and SELinux contexts may be lost on some distributions (notably, on CentOS/RHEL 7.x with coreutils ≤8.22).
- Inefficient across mountpoints: Moves between filesystems are implemented as copy+unlink, with visible performance impact and potential for partial state if interrupted.
- No native filtering or batch control.
Example: Unintended Overwrite
mv latest_report.csv /archive/
No error if /archive/latest_report.csv
exists—the old file is replaced without trace.
Interactive and Safe Moves: Avoiding Accidental Data Loss
Use -i
for interactive mode:
mv -i report.csv /backup/
If the file exists, a prompt appears:
mv: overwrite '/backup/report.csv'?
Alternatively, -n
disables overwrites entirely—useful for unattended scripts.
Flag | Purpose |
---|---|
-i | Prompt before overwrite |
-n | Never overwrite existing files |
-b | Create backups of overwritten |
Preserving Metadata and Attributions: Using rsync
for Moves
When the destination is a different filesystem, drop-in use of mv
is inadequate if original timestamps, permissions, or ownership must be retained. Instead:
rsync -a --remove-source-files file1 /mnt/backup/
-a
(archive): preserves permissions, times, ownership, links, devices, ACLs, xattrs.--remove-source-files
: deletes files from source after successful transfer.
Gotcha: Directories remain at the source—manually remove them post-transfer:
find /path/to/source -type d -empty -delete
In scripting, always validate transfer completion before cleanup to avoid data loss. NFS mounts, in particular, require explicit fsync on both ends for guarantees.
Selective and Conditional Moves: Beyond Basic Wildcards
Moving all .log
files larger than 1M with safety prompt:
find /var/log -type f -name "*.log" -size +1M -exec mv -i {} /mnt/long-term-logs/ \;
To avoid false positives on filenames containing spaces or special characters, prefer null separation:
find /input -type f -print0 | xargs -0 mv -t /output/
xargs -0
and-print0
handle nonstandard characters and newlines robustly.mv -t /output/
sets the target directory.
Test with dummy files containing spaces/hyphens:
touch "normal.log" "spaces and -weird.log"
This pattern is especially critical when batch-processing files from user uploads.
Handling Permissions and Ownership
User mismatch causes issues (mv
fails with Permission denied
). Use sudo
where justified, but bear in mind file ownership post-move:
sudo mv /tmp/build-artifacts /opt/releases/
ls -ld /opt/releases/build-artifacts
If UID/GID differences matter (e.g., dockerized environments), prefer rsync
plus explicit --chown
if supported (rsync
≥3.1.0):
sudo rsync -a --chown=deploy:deploy --remove-source-files /tmp/build-artifacts/ /opt/releases/build-artifacts/
sudo rm -rf /tmp/build-artifacts/
Known issue: Filesystem-level ACLs may require an explicit setfacl
step post-move.
Scripting Batch Operations: Practical Example
Batch-move .txt
files interactively, robust to spaces/special characters:
#!/bin/bash
src="/data/incoming"
dst="/data/ready"
mkdir -p "$dst"
find "$src" -maxdepth 1 -type f -name "*.txt" -print0 |
while IFS= read -r -d '' f; do
mv -i -- "$f" "$dst"
done
echo "Completed batch move at $(date)" >&2
--
signals end-of-options, preventing surprises if filenames start with-
.- Logging to stderr avoids contaminating stdout (handy in pipelines).
Non-Obvious Tip: Moving Open Files and Inodes
On systems with long-running processes (databases, loggers), moving a file does not affect processes holding the original file descriptor; the old inode persists until released.
Check with:
lsof | grep deleted
If logrotate is used, rely on copy-truncate or post-move signaling.
Summary: Advanced Techniques for Predictable File Moves
- Use
-i
,-n
, or-b
flags to prevent accidental overwrite. - Switch to
rsync -a --remove-source-files
for moves across filesystems or when metadata/ownership must be retained. - Leverage null-byte separation and
--
for filenames with nonstandard characters. - Verify successful transfer before cleanup—especially with NFS or unreliable links.
- Understand inode implications to avoid unexpected disk usage from moved-but-open files.
- Review permissions, especially in automated or root-driven jobs.
Perfect is the enemy of done: sometimes a sweep with mv
is enough, but for regulated environments or critical systems, always favor explicitness, backups, and dry runs.
Side note: On certain NAS appliances, both mv
and rsync
inherit quirks from vendor kernels—validate your approach before including in scheduled jobs.
Questions or edge-case scenarios? Review the coreutils and rsync man pages for options relevant to your distribution (--preserve-context
, --xattrs
, etc.). For security-centric filesystems, additional steps may be required.