cp Command Explained

cp Command Explained

cp copies files and directories. It is straightforward for a single file, but its behavior around directories, existing destinations, and metadata preservation has enough nuance to be worth covering properly.

Basic usage

cp source.txt destination.txt
cp report.txt ~/backups/
cp file1.txt file2.txt file3.txt ~/archive/

Given a single source and destination, cp duplicates the file under the new name or location. Given multiple sources and a final destination that is a directory, it copies each source file into that directory, keeping their original names.

Copying directories: the -r flag

cp project newcopy
# cp: -r not specified; omitting directory 'project'

cp -r project newcopy
# succeeds, copying the entire directory tree

Plain cp will not copy a directory at all without explicit permission to do so recursively. -r (or the equivalent -R) copies the directory along with everything nested inside it, preserving the internal structure.

Preserving metadata: cp -a

cp -r project backup
# copies content, but permissions/ownership/timestamps
# may not exactly match the original depending on context

cp -a project backup
# copies content AND preserves permissions, ownership,
# timestamps, and symbolic links exactly as they were

-a (archive mode) is effectively -r plus several preservation flags bundled together (--preserve=all in spirit). It is the standard choice when the goal is an exact duplicate rather than just a copy of the visible content, such as backing up a directory before a risky change, or migrating a directory tree between servers where ownership and permissions need to survive the move intact.

# A very common backup pattern before making risky changes
cp -a /etc/nginx /etc/nginx.bak.$(date +%F)

What happens when the destination already exists

cp newversion.txt oldversion.txt
# oldversion.txt is silently overwritten, no warning

By default, cp overwrites an existing destination file without asking. This is a frequent source of accidental data loss, particularly when a destination path was mistyped or when copying multiple files into a directory that already contains files with matching names.

# -i: prompt before overwriting each existing file
cp -i newversion.txt oldversion.txt
# cp: overwrite 'oldversion.txt'? y

# -n: never overwrite, silently skip if destination exists
cp -n newversion.txt oldversion.txt

-i is the safer interactive default for anyone who has been burned by an accidental overwrite before. -n is useful in scripts where you want copying to be a one-time operation that never clobbers something already in place.

Copying only newer files: -u

cp -u source/*.log destination/

-u (update) copies a file only when the source version is newer than what already exists at the destination, or when the destination file does not exist yet at all. This provides a lightweight form of incremental copying, useful for quick syncs, though rsync is the more capable and standard tool for anything beyond a simple case like this, since it can also handle deletions, partial transfers, and remote destinations.

Verbose output and dry runs

cp -v file1.txt file2.txt destination/
# 'file1.txt' -> 'destination/file1.txt'
# 'file2.txt' -> 'destination/file2.txt'

-v (verbose) prints a line for every file copied, which is useful for confirming exactly what happened in a bulk copy, particularly one involving wildcards or recursive directory copies where the full scope might not be obvious in advance.

Copying with a trailing slash: a subtle distinction

cp -r sourcedir destdir
# if destdir does not exist: destdir becomes a copy of sourcedir
# if destdir already exists: sourcedir is copied INTO destdir,
#                             becoming destdir/sourcedir

cp -r sourcedir/ destdir/
# behavior is largely the same; the trailing slash on the SOURCE
# generally has no special effect for cp (unlike rsync, where a
# trailing slash on the source changes the copy behavior significantly)

This distinction, whether the destination already exists as a directory, is one of the more common sources of confusion with cp -r, since the same command can produce a different resulting structure depending purely on whether the destination directory happened to already be present beforehand. Checking with ls destdir before running the copy avoids surprises.

Frequently Asked Questions

What does the cp command do?

cp copies a file or directory from a source location to a destination, leaving the original untouched. It is the standard way to duplicate files from the command line, whether copying a single file to a new name or copying an entire directory tree to a new location.

What does the -r flag do with cp?

Plain cp refuses to copy directories on their own, only regular files. Adding -r (recursive) tells cp to copy a directory along with every file and subdirectory nested inside it. Attempting cp somedir newdir without -r produces an error explaining that the source is a directory.

What is the difference between cp -r and cp -a?

cp -r copies directories recursively but does not necessarily preserve every attribute of the original files, such as ownership, permissions, or timestamps, depending on the system and defaults. cp -a (archive mode) is a more thorough option that copies recursively while also preserving permissions, ownership, timestamps, and symbolic links exactly as they were, making it the standard choice for backups and full directory migrations where preserving metadata matters.

How do I stop cp from overwriting an existing file?

Use the -n flag (no clobber), which skips copying if the destination file already exists rather than overwriting it silently. Alternatively, -i (interactive) prompts for confirmation before overwriting each existing file, giving you a chance to review before anything is replaced.

Why did my cp command overwrite a file I did not mean to replace?

By default, cp overwrites an existing destination file without warning, on the assumption that if you specified that destination, you intended to replace whatever was already there. This is a common source of accidental data loss when a destination path is mistyped or a wildcard expands unexpectedly. Using cp -i by default, or checking the destination with ls first, prevents this.

How do I copy only files that are newer than what already exists at the destination?

Use the -u flag (update), which copies a file only if the source is newer than an existing destination file, or if the destination does not exist at all. This is useful for incremental copies where you want to sync a folder without re-copying files that have not changed since the last time, though for more sophisticated incremental syncing, rsync is generally the better tool.