Linux file commands are easy to memorize and just as easy to misuse. Most accidents do not happen because someone forgets that mv moves a file or that rm deletes one. They happen because the destination already exists, a variable is empty, a path contains spaces, or an rsync --delete command points at the wrong directory.
The safest routine is straightforward:
- Inspect the source and destination.
- Preview the operation where possible.
- Make the change.
- Verify the result.
- Delete the old data only after verification.
That process takes a few extra seconds and is considerably faster than restoring files from backup.
This guide was updated for Ubuntu 26.04 LTS and rsync 3.4.1. Ubuntu 26.04 uses Rust-based uutils for many core commands, but cp, mv and rm continue using GNU Coreutils due to unresolved compatibility and security issues. The Ubuntu 26.04 repositories provide rsync 3.4.1.
Several commands below use GNU or non-POSIX options. These are identified where relevant.
Linux File Command Quick Reference
| Job | Safer command | Important detail |
|---|---|---|
| Rename a directory | mv -iT -- old-name new-name | -T prevents the destination from being treated as a directory to move into |
| Move a file | mv -iv -- report.txt /srv/reports/ | -i asks before overwriting an existing file |
| Archive-copy a directory | cp -aivT -- app app.backup | -a preserves supported metadata; -T prevents accidental nesting |
| Preview an rsync copy | rsync -a --dry-run --itemize-changes -- source/ destination/ | The source slash means “copy the contents” |
| Verify an rsync copy | rsync -a --dry-run --checksum --itemize-changes -- source/ destination/ | Reads both copies and compares file contents |
| Preview a mirror | rsync -a --dry-run --itemize-changes --delete -- source/ mirror/ | Destination-only files will be removed during the real run |
| Remove a directory tree | rm -Ir --one-file-system -- old-release | GNU options; review the prompt carefully |
The standalone -- argument ends option processing. It prevents a filename such as -draft from being interpreted as a command-line option.
Quoting every variable and pathname is equally important:
mv -- "$source" "$destination"
The quotes preserve spaces and shell metacharacters in the path. The -- protects names beginning with a hyphen.
Check Which Command Implementation You Are Using
Before relying on implementation-specific options, check the installed versions:
cp --version | head -n 1
mv --version | head -n 1
rm --version | head -n 1
rsync --version | head -n 1
On a standard Ubuntu 26.04 installation, cp, mv and rm should report GNU Coreutils. Other commands may report uutils coreutils.
You can also inspect the executable selected by your shell:
command -v cp
command -v mv
command -v rm
command -v rsync
Options such as cp -T, mv -T, rm --one-file-system and GNU find -printf should not be assumed to exist on every Unix-like system. Check the local manual before using them in portable scripts.
Before Changing Anything
Confirm your current location and inspect both sides of the proposed operation:
pwd
ls -lah
ls -ld -- source destination
find source -maxdepth 2 -printf '%y %m %u:%g %s %p\n' | sort
These commands answer different questions:
pwdconfirms your current directory.ls -lahshows the surrounding files, including hidden entries.ls -ldinspects the named directory entries rather than listing their contents.findproduces a compact view of the source tree.
The find -printf format above displays:
- File type
- Permission mode
- Owner and group
- File size
- Full pathname
find -printf is a GNU Findutils extension and is not portable across all Unix implementations.
For important data, also check the source and destination filesystems:
df -hT -- source destination
A cross-filesystem mv behaves differently from a rename on the same filesystem. Instead of changing a directory entry, it must copy the data and then remove the source.
Rename a Directory Without Moving It Inside Another Directory
A normal two-argument mv command has an important ambiguity:
mv project-old project-archive
When project-archive already exists as a directory, mv may place project-old inside it:
project-archive/project-old/
On GNU systems, use -T or --no-target-directory when the destination must be treated as the exact destination entry:
mv -iT -- project-old project-archive
The options mean:
-iprompts before replacing an existing destination.-Ttreats the final operand as the destination itself.--ends option parsing.
GNU documents -T as the option that prevents the last operand from being treated specially when it names a directory or a symbolic link to a directory.
Be aware that -T prevents accidental nesting; it does not guarantee that the destination is absent. For example, mv may replace an existing empty destination directory after confirmation.
Rename a Directory in a Script
Interactive prompts are unsuitable for unattended scripts. Validate the source and destination explicitly:
#!/bin/sh
set -eu
source='project-old'
destination='project-archive'
if [ ! -e "$source" ] && [ ! -L "$source" ]; then
printf 'Source does not exist: %s\n' "$source" >&2
exit 1
fi
if [ -e "$destination" ] || [ -L "$destination" ]; then
printf 'Refusing to replace existing destination: %s\n' \
"$destination" >&2
exit 1
fi
mv -T -- "$source" "$destination"
Testing both -e and -L matters because -e returns false for a dangling symbolic link. Without the -L check, a broken destination symlink could go unnoticed.
This check-and-move pattern is appropriate for normal administrative scripts, but it is not safe against every concurrency race. Another process could create the destination between the test and the mv command. Workflows with concurrent writers require locking or a deployment mechanism designed for atomic switching.
-T is not a POSIX option. Test scripts against every operating system they must support.
Move a File Into a Known Directory
To move a file into an existing directory:
mv -iv -- report.txt /srv/reports/
Before running it, confirm that the destination exists and is the expected directory:
ls -ld -- /srv/reports/
The trailing slash communicates that /srv/reports/ is intended to be a directory. If it does not exist, mv should fail rather than silently creating a file called reports.
For multiple source files on GNU systems, -t makes the target directory explicit:
mv -iv -t /srv/reports/ -- report-1.txt report-2.txt
The -t form is useful in scripts because the destination is supplied as an option rather than inferred from the final operand.
Copy a Directory While Preserving Metadata
For an archive-style copy on GNU/Linux, use cp -a rather than a basic recursive copy:
cp -aivT -- app app.backup
The options perform the following work:
-aenables archive mode.-iasks before overwriting existing entries.-vdisplays copied paths.-Ttreatsapp.backupas the exact destination.
GNU describes cp -a as equivalent to recursive copying with link and metadata preservation enabled. It attempts to preserve permissions, ownership, timestamps, links, ACL-related mode information, extended attributes and security context where supported.
Why -T Matters for cp
Without -T, this command:
cp -aiv -- app app.backup
can create the following layout when app.backup already exists:
app.backup/app/
Adding -T prevents that extra directory level:
cp -aivT -- app app.backup
However, -T does not make the destination exclusive. If app.backup already exists as a directory, cp can merge the source tree into it. The -i option prompts only when an existing entry would be overwritten.
For a new standalone backup, use a unique destination:
backup="app.backup.$(date +%Y%m%d-%H%M%S)"
cp -aT -- app "$backup"
Then inspect the result:
ls -ld -- "$backup"
find "$backup" -maxdepth 2 -printf '%y %m %u:%g %s %p\n' | sort
Metadata Preservation Has Limits
Archive mode cannot guarantee that every attribute will survive every copy.
Results depend on:
- The source and destination filesystems
- User privileges
- ACL support
- Extended-attribute support
- SELinux or other security labeling
- User and group availability
- Filesystem-specific flags
- Whether the destination supports hard links, sparse files and special files
GNU also notes that cp -a can ignore some failures to preserve SELinux context or extended attributes without changing the command’s exit status. When metadata is operationally important, inspect it separately rather than relying only on a successful exit code.
Useful inspection commands include:
stat -- app app.backup
getfacl -R -- app
getfacl -R -- app.backup
getfattr -R -d -m- -- app
getfattr -R -d -m- -- app.backup
The acl and attr packages may be required for getfacl and getfattr.
Use rsync for Repeatable Copies
rsync is usually a better choice when you expect to run the same copy more than once. Ubuntu 26.04 provides rsync 3.4.1.
Install it when necessary:
sudo apt update
sudo apt install rsync
Always preview a meaningful rsync command before running it:
rsync -a --dry-run --itemize-changes -- source/ destination/
The options mean:
-aenables rsync archive mode.--dry-runcalculates the changes without applying them.--itemize-changesexplains what would change.--ends option parsing.
The rsync manual recommends combining --dry-run with verbose or itemised output so that the proposed operation can be reviewed before it runs.
When the preview is correct, remove --dry-run:
rsync -a --itemize-changes -- source/ destination/
This copies new and changed source files. It does not remove destination-only files, so it is an update operation rather than a true mirror.
Understand the rsync Source Trailing Slash
The trailing slash on the source changes the resulting directory layout.
Copy the Contents of the Source Directory
rsync -a -- source/ destination/
This produces:
destination/file.txt
Copy the Source Directory by Name
rsync -a -- source destination/
This normally produces:
destination/source/file.txt
The rsync documentation describes a trailing source slash as copying the directory’s contents instead of creating an additional directory level at the destination.
Practise this behaviour in a disposable directory before using rsync against production data. A misplaced slash is one of the most common causes of incorrect directory layouts.
Also ensure the destination directory exists. With a single matching source item, an absent destination path can sometimes be interpreted as a new filename rather than a directory name.
Know What rsync -a Does Not Preserve
Rsync archive mode is equivalent to:
-rlptgoD
Despite its name, -a does not preserve every available attribute. It does not automatically include:
- ACLs
- Extended attributes
- Hard-link relationships
- Access times
- Creation times on systems that support them
The rsync manual explicitly excludes -A, -X, -H, -U and -N from archive mode.
When ACLs, extended attributes, and hard-link relationships matter, preview the following form:
rsync -aHAX --dry-run --itemize-changes -- source/ destination/
Then run it without --dry-run:
rsync -aHAX --itemize-changes -- source/ destination/
The additional options are:
-H: preserve hard-link relationships.-A: preserve ACLs.-X: preserve extended attributes.
Preserving ownership may require root privileges on the receiving side. Hard-link detection can also increase memory use on very large directory trees.
For migrations between systems where numeric ownership must remain unchanged, consider:
rsync -aHAX --numeric-ids --dry-run --itemize-changes -- \
source/ destination/
Only use --numeric-ids when identical numeric user and group IDs have the intended meaning on both systems.
Verify the rsync Result
A second normal dry run checks whether rsync still detects changes:
rsync -a --dry-run --itemize-changes -- source/ destination/
No output usually means rsync considers the source and destination current according to its normal quick check, which compares file size and modification time.
For a stronger one-off content check, add --checksum:
rsync -a --dry-run --checksum --itemize-changes -- \
source/ destination/
The --checksum option reads corresponding files and uses content checksums to decide whether they differ. This is more expensive because every candidate file must be read on both sides. Rsync already verifies transferred files as part of the transfer protocol; --checksum changes the pre-transfer comparison used to decide whether a file needs updating.
For smaller directory trees, diff is another useful content check:
diff -qr -- source destination
diff -qr compares file contents but does not verify every metadata attribute.
Mirror a Directory With --delete
The --delete option removes destination entries that do not exist in the source:
rsync -a --delete -- source/ mirror/
That is appropriate for a mirror. It is dangerous for an ordinary backup directory that contains historical or destination-only files.
Always preview deletions:
rsync -a --dry-run --itemize-changes --delete -- \
source/ mirror/
Only run the real operation after reviewing every proposed deletion:
rsync -a --itemize-changes --delete -- \
source/ mirror/
The rsync manual describes --delete as dangerous when used incorrectly and recommends a dry run before allowing deletions.
Confirm That the Source Is Mounted and Populated
An empty or incorrectly mounted source combined with --delete can create a very accurate empty mirror.
Inspect both paths first:
ls -ld -- source mirror
find source -mindepth 1 -maxdepth 1 -print
df -hT -- source mirror
For scheduled jobs, validate an expected marker file:
source='/mnt/data-source'
mirror='/srv/data-mirror'
if [ ! -d "$source" ]; then
printf 'Source directory is unavailable: %s\n' "$source" >&2
exit 1
fi
if [ ! -f "$source/.rsync-source-ok" ]; then
printf 'Source marker is missing: %s\n' "$source" >&2
exit 1
fi
if [ ! -d "$mirror" ]; then
printf 'Mirror directory is unavailable: %s\n' "$mirror" >&2
exit 1
fi
rsync -a --dry-run --itemize-changes --delete -- \
"$source/" "$mirror/"
The marker should be created deliberately on the real source filesystem:
touch /mnt/data-source/.rsync-source-ok
If the mount fails and exposes an empty underlying directory, the marker will be absent and the job will stop.
Limit the Number of Deletions
For automated mirrors, --max-delete limits the deletion blast radius:
rsync -a --dry-run --itemize-changes \
--delete \
--max-delete=100 \
-- source/ mirror/
During the real run, rsync stops deleting after the configured limit is reached, reports the skipped deletions and exits with status 25. Transfers and updates that occurred before the limit was reached may still have been applied, so your monitoring must treat the non-zero exit as a failure.
Choose a limit that is low enough to catch unexpected source failures but high enough for legitimate cleanup.
Delete Last, Not First
Before removing a directory tree, inspect it one final time:
ls -ld -- old-release
find old-release -maxdepth 2 -printf '%y %m %u:%g %s %p\n' | sort
du -sh -- old-release
For an interactive GNU/Linux session, the following command is safer than immediately using rm -rf:
rm -Ir --one-file-system -- old-release
The options mean:
-Iprompts once before a recursive removal or when more than three files are named.-rremoves the directory tree recursively.--one-file-systemavoids descending into directories on a different filesystem.--ends option parsing.
GNU documents -I as a single confirmation before a recursive operation. It also describes --one-file-system as protection against recursively entering a separately mounted filesystem beneath the target.
These controls reduce risk, but they do not make deletion reversible.
--one-file-system will not protect:
- Other valuable directories on the same filesystem
- An incorrectly selected parent directory
- Data referenced through every possible mount arrangement
- Files already removed before an error occurs
When the data cannot be recreated, make and test a backup before deleting it.
Guard Against Empty Variables in Removal Scripts
Never place an unchecked variable directly into a recursive removal command:
rm -rf -- "$target"
Validate it first:
#!/bin/sh
set -eu
target='/srv/releases/old-release'
case "$target" in
'' | '/' | '.' | '..')
printf 'Refusing unsafe removal target: %s\n' "$target" >&2
exit 1
;;
esac
if [ ! -d "$target" ]; then
printf 'Removal target is not a directory: %s\n' "$target" >&2
exit 1
fi
printf 'Removing directory tree: %s\n' "$target"
rm -Ir --one-file-system -- "$target"
For unattended automation, an interactive prompt is not sufficient. Use an explicit allowlist, expected path prefix, marker file, locking, and a tested backup or rollback process.
Complete Disposable Test
The following sequence creates a temporary workspace, copies a directory, renames the copy, previews an rsync operation, performs it, and verifies the result:
workdir=$(mktemp -d)
mkdir -p \
"$workdir/source" \
"$workdir/destination"
printf 'alpha\n' > "$workdir/source/alpha.txt"
printf 'beta\n' > "$workdir/source/file with spaces.txt"
# Create an archive-style copy at an exact destination path
cp -aivT -- \
"$workdir/source" \
"$workdir/source-copy"
# Rename the copied directory
mv -iT -- \
"$workdir/source-copy" \
"$workdir/renamed-copy"
# Preview the repeatable copy
rsync -a \
--dry-run \
--itemize-changes \
-- "$workdir/source/" "$workdir/destination/"
# Apply the copy
rsync -a \
--itemize-changes \
-- "$workdir/source/" "$workdir/destination/"
# Verify contents using checksums
rsync -a \
--dry-run \
--checksum \
--itemize-changes \
-- "$workdir/source/" "$workdir/destination/"
# Inspect the final tree
find "$workdir" \
-maxdepth 3 \
-printf '%y %m %u:%g %s %p\n' |
sort
The first rsync dry run should list both newly created files with output similar to:
>f+++++++++ alpha.txt
>f+++++++++ file with spaces.txt
The checksum verification should produce no output after a successful copy.
Inspect the workspace before removing it:
ls -ld -- "$workdir"
find "$workdir" -maxdepth 3 -printf '%y %p\n' | sort
Then remove the temporary directory:
rm -Ir --one-file-system -- "$workdir"
Final Safety Checklist
Before changing important files, confirm the following:
- You are in the expected directory.
- The source exists and contains the expected data.
- The destination path is correct.
- Variables are quoted and non-empty.
--separates options from pathnames.-Tis used when the destination must be an exact entry.- The trailing slash on the rsync source matches the intended layout.
- Every
rsync --deleteoperation has been previewed. - Automated mirrors validate their source mount or marker file.
- Important copies have been verified.
- A usable backup exists before permanent deletion.
Primary Documentation
- Ubuntu 26.04 LTS release notes, including the
rust-coreutilstransition and continued use of GNUcp,mvandrm. - GNU Coreutils manual covering
cp,mv,rm, target-directory handling and metadata preservation. - Official rsync manual covering archive mode, dry runs, trailing slashes, checksums and deletion controls.
- Ubuntu 26.04 rsync package information.
Related TurboGeek Guides
- How to Rename a Directory in Linux
- How to Copy a Directory in Linux
- How to Delete Files Safely in Linux
- How to Find What Is Using Disk Space in Linux


Leave a Reply