opscape

Files and directories

cp

Copy files and directories, optionally keeping their timestamps, permissions and links.

cp [OPTION...] SOURCE... DEST

12 of 12 examples run in Debian 13.7 on 2026-10-31 · coreutils 9.7-3 · manual: cp(1)

cp SOURCE DEST copies one file. With several sources, or when DEST is an existing directory, the copies go into that directory under their own names. Directories need -r (recursive).

A plain copy is a new file: owned by you, with a fresh modification time. -p keeps the mode, owner (when allowed) and timestamps; -a (archive) does that recursively and also copies symlinks as links, which makes it the right choice for backups.

cp overwrites existing files silently. -i asks first, -n never overwrites, and -u only copies when the source is newer. For large or remote copies, or to sync a directory repeatedly, rsync is more capable.

Examples

Copy a file

cp notes.txt notes-backup.txt
ls -l notes*
output
-rw-r--r-- 1 dev dev 190 Oct 31 20:19 notes-backup.txt
-rw-rw-r-- 1 dev dev 190 Oct 25 05:30 notes.txt

The copy has the same size but a new modification time: plain cp does not preserve timestamps.

Copy files into a directory

mkdir backup
cp todo.txt notes.txt backup/
ls backup
output
notes.txt
todo.txt

With more than one source, the destination must be a directory; each file keeps its name. The trailing slash is optional but makes the intent clear and turns a typo into an error.

Copy a whole directory

cp -r src src-copy
find src-copy
output
src-copy
src-copy/Makefile
src-copy/util.sh
src-copy/main.c
src-copy/app.py
src-copy/lib
src-copy/lib/helpers.py

-r copies the directory tree. Because src-copy did not exist, it becomes the copy. Had it existed, the result would be src-copy/src, a classic surprise.

Copying a directory without -r

cp logs logs-old
explain
outputexit 1
cp: -r not specified; omitting directory 'logs'

cp skips directories unless you ask for a recursive copy.

Keep the original timestamps

cp todo.txt plain.txt
cp -p todo.txt kept.txt
ls -l --time-style=+%F_%T todo.txt plain.txt kept.txt
output
-rw-rw-r-- 1 dev dev 62 2026-10-24_21:28:49 kept.txt
-rw-r--r-- 1 dev dev 62 2026-10-31_20:19:31 plain.txt
-rw-rw-r-- 1 dev dev 62 2026-10-24_21:28:49 todo.txt

plain.txt gets the current time; kept.txt keeps the original modification time (and mode) because of -p.

Copy a tree exactly, links included

cp -a . ../demo-copy
ls -l ../demo-copy/link-to-notes
output
lrwxrwxrwx 1 dev dev 9 Oct 25 02:48 ../demo-copy/link-to-notes -> notes.txt

-a (archive) is -dR --preserve=all: recursive, timestamps and permissions kept, and symbolic links copied as links rather than as the files they point to.

Never overwrite an existing file

Setup run before this example
echo "my local edits" > notes-copy.txt
cp -n notes.txt notes-copy.txt
cat notes-copy.txt
output
my local edits

-n skips destinations that already exist, so the local edits survive. In coreutils 9.7 a skipped file is not an error, so the exit status is 0.

Ask before overwriting

cp -i todo.txt notes.txt
head -1 notes.txt
output
cp: overwrite 'notes.txt'? Meeting notes, March

-i prompts when the target exists. The answer n (from standard input here) keeps notes.txt as it was.

Keep a backup of the file being replaced

Setup run before this example
echo "new notes" > incoming.txt
cp --backup=numbered incoming.txt notes.txt
cp --backup=numbered incoming.txt notes.txt
ls notes.txt*
output
notes.txt
notes.txt.~1~
notes.txt.~2~

Instead of losing the old notes.txt, each overwrite renames it to notes.txt.~1~, notes.txt.~2~ and so on. -b alone makes a single notes.txt~.

Copy only when the source is newer

Setup run before this example
mkdir out && cp -p data/*.csv out/
touch data/users.csv
cp -uv data/*.csv out/
explain
output
'data/users.csv' -> 'out/users.csv'

-u skips files whose destination is the same age or newer. Only users.csv was modified since the last copy, so it is the only one copied (-v shows it).

Copy files and recreate their directory paths

mkdir bundle
cp --parents src/lib/helpers.py data/users.csv bundle/
find bundle -type f
output
bundle/src/lib/helpers.py
bundle/data/users.csv

--parents keeps each source's relative path under the destination, handy for picking a few files out of a tree.

Copy a symlink's target, not the link

cp link-to-notes copy1
cp -P link-to-notes copy2
ls -l copy1 copy2
output
-rw-r--r-- 1 dev dev 190 Oct 31 20:19 copy1
lrwxrwxrwx 1 dev dev   9 Oct 31 20:19 copy2 -> notes.txt

For a single file, cp follows the symlink and copies the content (copy1 is a regular file). -P copies the link itself, so copy2 is a new link to notes.txt.

Common pitfalls

  • cp -r dir dest behaves differently depending on whether dest exists: it creates dest, or copies into dest/dir. Use cp -r dir/. dest/ to copy the contents into an existing directory either way.

  • Forgetting the destination: cp *.txt with one match fails with "missing destination file operand", but with exactly two matches it silently overwrites the second file with the first. Always name the destination.

  • Symlink handling depends on the options: without -r, GNU cp follows a symlink and copies the file; with -r it copies links as links. Say what you mean with -P (keep links), -L (copy targets) or -a.

GNU vs BSD and macOS

The examples above ran the GNU/Linux version. These are the differences that matter on macOS and the BSDs (written from their documentation, not run here).

  • -a is supported by both, but GNU-only options include --parents, --backup, -t DIR and --reflink; macOS has its own -c for copy-on-write clones.

  • Trailing slash on the source matters on BSD/macOS: cp -R src/ dest copies the contents of src, while GNU copies the directory itself either way.

  • cp -n exit status changed in GNU coreutils 9.x releases; do not rely on it to detect skipped files across versions.

Options worth knowing

A selection, described in our own words. For the full list, see cp(1) or cp --help.

OptionWhat it does
-r, --recursiveCopy directories recursively.
-RSame as -r.
-a, --archiveRecursive, keep links, modes, owners and timestamps.
-pKeep mode, ownership and timestamps.
-i, --interactiveAsk before overwriting.
-n, --no-clobberNever overwrite an existing file.
-f, --forceRemove a destination that cannot be opened and try again.
-uCopy only when the source is newer or the destination is missing.
--update[=UPDATE]Choose which existing files to replace: all, none, none-fail or older.
-v, --verbosePrint each file as it is copied.
-l, --linkMake hard links instead of copies.
-s, --symbolic-linkMake symbolic links instead of copies.
-L, --dereferenceAlways follow symlinks in the sources.
-P, --no-dereferenceCopy symlinks as links.
-t, --target-directory DIRCopy all sources into DIR.
-T, --no-target-directoryTreat DEST as a file name, never a directory.
-bBack up each existing destination file.
--backup[=CONTROL]Back up existing files: numbered, existing, simple or none.
--parentsRecreate each source's path under the destination directory.
--preserve[=ATTRS]Keep the listed attributes: mode, ownership, timestamps, links, all.
--reflink[=WHEN]Make a copy-on-write clone where the filesystem supports it.
--sparse WHENControl creation of sparse files: auto, always or never.
-x, --one-file-systemStay on the filesystem where each source starts.

Related