LFCA 15 ๐ง Copying Files and Directories โ cp
cp copies files and directories. It reads the source, writes a new copy at the destination, and leaves the original untouched. It’s one of the commands you’ll use most often โ duplicating configs before editing, backing up files, moving data into a project directory, populating a template. On the surface it’s simple: cp SOURCE DEST. Underneath there are nuances โ how it handles directories, symlinks, permissions, timestamps, existing files, and the difference between copying a file into a directory versus onto a name. The LFCA exam expects you to know cp cold.
Key point: cp copies โ it doesn’t move. The source stays. The destination is a new file or directory with the same content. For files, cp SOURCE DEST writes a copy. For directories, you need -r (recursive). The trailing slash on a destination directory matters: cp file dir/ copies into the directory; cp file dir overwrites the file named dir if it exists. Get the destination semantics right and cp is predictable; get them wrong and you’ll either overwrite the wrong thing or fail with a confusing error.
What cp does
cp reads the source and writes a new copy at the destination. The original is unchanged.
$ echo "hello" > original.txt
$ cp original.txt copy.txt
$ cat original.txt
hello
$ cat copy.txt
hello
Both files exist with the same content. The original is untouched.
What cp copies:
- File contents
- Permissions (usually, depending on flags)
- Timestamps (only with
-p) - Ownership (only with
-pand root) - Extended attributes (with
-aor specific flags)
What cp doesn’t do:
- Doesn’t move the original โ that’s
mv - Doesn’t delete the source
- Doesn’t create parent directories by default
- Doesn’t follow or not follow symlinks the way you might expect without flags
The basic forms:
| Form | Meaning |
|---|---|
cp file1 file2 | Copy file1 to file2 |
cp file1 dir/ | Copy file1 into dir/ |
cp file1 file2 dir/ | Copy multiple files into dir/ |
cp -r dir1 dir2 | Copy dir1 to dir2 (recursive) |
cp -r dir1/ dir2/ | Copy contents of dir1 into dir2/ |
Destination semantics: The most important thing to understand about cp.
- If the destination is a directory, the source is copied into it (with the same basename)
- If the destination is a name that doesn’t exist, a new file or directory is created with that name
- If the destination is an existing file,
cpoverwrites it (unless-ior-n)
$ cp file.txt /tmp/ # copies to /tmp/file.txt
$ cp file.txt newname.txt # copies to ./newname.txt
$ cp file.txt existing.txt # overwrites existing.txt
Why the distinction matters: Most cp mistakes are destination mistakes โ copying into the wrong place, overwriting a file you wanted to keep, or failing because the destination didn’t exist. Understanding the three cases prevents all of them.
Why
cpandmvare separate:cppreserves the original;mvmoves it. They’re different operations with different costs (copying is I/O-heavy; moving is a rename). Keeping them separate matches the Unix philosophy โ one command, one purpose.
Copying files
The simplest case: copying one file to another.
Copy to a new name in the same directory:
$ cp report.txt report-backup.txt
$ ls
report-backup.txt report.txt
Copy to a different directory:
$ cp report.txt /tmp/
$ ls /tmp/report.txt
/tmp/report.txt
When the destination is a directory, the file keeps its name.
Copy to a different directory with a new name:
$ cp report.txt /tmp/summary.txt
$ ls /tmp/summary.txt
/tmp/summary.txt
Copy multiple files to a directory:
$ cp file1.txt file2.txt file3.txt /tmp/
$ ls /tmp/file*.txt
/tmp/file1.txt /tmp/file2.txt /tmp/file3.txt
The last argument must be a directory. Each source is copied into it.
Copy with a wildcard:
$ cp *.txt /tmp/
The shell expands *.txt into multiple filenames; cp copies them all into /tmp/.
Overwriting an existing file:
$ echo "original" > dest.txt
$ cp source.txt dest.txt
$ cat dest.txt
(from source.txt)
By default, cp overwrites silently. Use -i to prompt or -n to skip.
What cp does to permissions: By default, cp creates the destination with the source’s permissions modified by the umask. To preserve exactly, use -p.
$ ls -l source.txt
-rwxr-xr-x 1 alice alice 0 Sep 22 10:30 source.txt
$ cp source.txt dest.txt
$ ls -l dest.txt
-rwxr-xr-x 1 alice alice 0 Sep 22 10:30 dest.txt
In this case permissions match because the umask doesn’t strip anything from the source. If the source has unusual permissions, cp -p is needed to preserve them exactly.
Why the umask applies: New files inherit the umask. Even when copying, the destination is a new file โ the umask masks the mode. -p overrides this.
Why “copy to a directory” is the common case: You rarely copy a file to another name in the same directory. You usually copy it elsewhere โ a backup directory, a temp directory, another project. The destination-is-a-directory form handles that cleanly.
cp -i, -n, -u โ handling existing files
By default, cp overwrites existing files silently. Three flags change this.
-i โ interactive:
$ cp -i source.txt dest.txt
cp: overwrite 'dest.txt'? y
Prompts before overwriting. The answer must start with y or Y to proceed.
-n โ no clobber:
$ cp -n source.txt dest.txt
$ echo $?
0
Doesn’t overwrite. If the destination exists, cp silently skips it.
-u โ update only:
$ cp -u source.txt dest.txt
Copies only if the source is newer than the destination, or if the destination doesn’t exist. Useful for sync-like behavior.
When to use each:
| Flag | Use case |
|---|---|
-i | Interactive โ you want to confirm each overwrite |
-n | Scripting โ never overwrite |
-u | Sync โ only copy newer files |
The alias trap: Many systems alias cp to cp -i:
$ alias cp
alias cp='cp -i'
In interactive shells, cp prompts. In scripts, aliases don’t apply โ cp overwrites silently. That’s a common source of surprise: the same command behaves differently in a script.
To bypass an alias: \cp or command cp or the full path /bin/cp.
Why -i is often aliased: Accidental overwrites are a common mistake. Aliasing cp to cp -i protects against them. But it only affects interactive shells โ scripts see the plain cp.
Why -n is safer in scripts: A script shouldn’t prompt โ there’s no one to answer. -n makes the behavior deterministic: never overwrite. If the script needs to overwrite, it shouldn’t use -n.
Why -u exists: It’s a poor man’s sync. Copy only what’s changed. For real sync, use rsync, but cp -u covers the simple case.
Why “clobber” is the term: “Clobber” means to overwrite destructively.
-n(no clobber) is the standard Unix term for “don’t overwrite.” The wording is old but consistent across tools.
Copying directories with -r
cp needs -r (or -R, --recursive) to copy a directory.
$ cp -r source-dir dest-dir
Without -r:
$ cp source-dir dest-dir
cp: -r not specified; omitting directory 'source-dir'
cp refuses to copy a directory unless told to recurse.
What -r does: Copies the directory and everything inside it โ subdirectories, files, symlinks โ recursively.
The destination rules for directories:
If the destination doesn’t exist:
$ cp -r source dest
# Creates dest/ with source's contents
# dest/source equivalent โ no, dest IS the copy
The result: dest/ contains everything source/ had. source/ still exists.
If the destination exists as a directory:
$ cp -r source dest/
# Creates dest/source/ with source's contents
The source directory is copied inside dest/. Result: dest/source/.
This is the subtle part. The presence of the destination determines the behavior:
cp -r source dest(dest doesn’t exist) โdestbecomes the copy ofsourcecp -r source dest(dest exists) โsourceis copied intodest, producingdest/sourcecp -r source/. destโ copies the contents ofsourceintodest, not the directory itself
Copying contents, not the directory:
$ mkdir dest
$ cp -r source/. dest/
# dest/ now contains everything inside source/
# no dest/source subdirectory
The /. at the end of the source means “the contents of this directory.” That’s the standard idiom for copying contents into an existing directory without nesting.
Examples:
$ mkdir -p a/b/c
$ touch a/b/c/file.txt
$ cp -r a copy1
$ find copy1
copy1
copy1/b
copy1/b/c
copy1/b/c/file.txt
$ mkdir existing
$ cp -r a existing
$ find existing
existing
existing/a
existing/a/b
existing/a/b/c
existing/a/b/c/file.txt
First case: dest was new, so it becomes a copy of source. Second: dest existed, so source was nested inside.
Why the difference: cp follows the same rule as with files. If the destination is a directory, the source is copied into it. If the destination is a name, the copy becomes that name. Directories aren’t special โ the rule is uniform.
Why the “contents” idiom exists: Often you want to copy what’s inside a directory into another directory, not nest one inside the other.
cp -r source/. dest/says “all of source’s entries, into dest.” The/.is how you express “the contents” rather than “the directory.”
Preserving attributes with -p and -a
By default, cp creates new files with new metadata. -p preserves the important attributes; -a preserves everything.
-p โ preserve:
$ cp -p source.txt dest.txt
Preserves:
- Permissions (mode)
- Ownership (if permitted โ root only)
- Timestamps (atime, mtime)
- Extended attributes (on some systems)
What -p doesn’t preserve:
- The inode number (always new)
- Hard links (each is copied as a separate file)
- SELinux contexts (in some configurations)
-a โ archive:
$ cp -a source dest
-a is equivalent to -dR --preserve=all. It preserves everything -p does, plus recurses and preserves symlinks as symlinks.
What -a preserves:
- Permissions, ownership, timestamps
- Symlinks as symlinks (not copied as regular files)
- Hard links (preserved as hard links within the copy)
- Extended attributes, ACLs, SELinux contexts
When to use which:
| Task | Flag |
|---|---|
| Copy config, keep perms | -p |
| Backup a directory | -a |
| Copy a project tree | -a |
| Copy a file with new timestamps | (none) |
Example of timestamp preservation:
$ ls -l source.txt
-rw-r--r-- 1 alice alice 123 Jan 15 09:00 source.txt
$ cp source.txt dest.txt
$ ls -l dest.txt
-rw-r--r-- 1 alice alice 123 Sep 22 10:30 dest.txt
# (new timestamp)
$ cp -p source.txt dest2.txt
$ ls -l dest2.txt
-rw-r--r-- 1 alice alice 123 Jan 15 09:00 dest2.txt
# (original timestamp preserved)
Symlink handling: By default, cp follows symlinks โ copying the target’s content. -P or -a preserves them as symlinks.
$ ln -s target.txt link.txt
$ cp link.txt copy.txt
# copy.txt is a regular file with target.txt's content
$ cp -P link.txt copy2.txt
# copy2.txt is a symlink pointing to target.txt
$ cp -a link.txt copy3.txt
# copy3.txt is a symlink (same as -P)
Why -a is called “archive”: It’s designed for backups โ copy a tree with everything preserved exactly. The name comes from tar-style archiving: keep all attributes.
Why ownership preservation needs root: Only root can change file ownership. A regular user’s
cp -ppreserves permissions and timestamps but not owner (since the new file will be owned by the copying user). Root can preserve ownership because root can chown.
Copying symlinks
Symlinks introduce a choice: copy the link itself, or copy the target?
By default โ follow (copy the target):
$ ln -s /etc/hosts mylink
$ cp mylink mycopy
$ ls -l mycopy
-rw-r--r-- 1 alice alice 234 Sep 22 10:30 mycopy
mycopy is a regular file with /etc/hosts‘s content. The symlink wasn’t copied โ its target was.
With -P or -d โ no dereference:
$ cp -P mylink mylinkcopy
$ ls -l mylinkcopy
lrwxrwxrwx 1 alice alice 10 Sep 22 10:30 mylinkcopy -> /etc/hosts
mylinkcopy is a symlink pointing to /etc/hosts.
With -a or -dR โ same as -P:
-a implies -P and -d, so symlinks are preserved.
With -L โ force follow:
$ cp -L mylink copy
-L forces following even if -P was implied by another flag.
The flags:
| Flag | Behavior |
|---|---|
| (default) | Follow symlinks โ copy the target |
-P | Never follow โ copy the symlink |
-L | Always follow โ copy the target |
-a | Implies -P โ preserve symlinks |
-d | Same as -P --no-dereference --preserve=links |
When each matters:
- Default โ you want the content, and you don’t care that the original was a symlink
-Pโ you want an exact replica of the tree, including symlinks-Lโ you want to resolve all symlinks into regular files
Why -a preserves symlinks: It’s for backups. A backup that dereferences symlinks loses information โ the restored tree wouldn’t match the original. -a keeps symlinks as symlinks.
Why -P in a project copy: A project might have symlinks (e.g., node_modules/.bin/*). Copying with -P preserves the structure. Copying with the default would dereference them, creating separate copies of the targets โ often not what you want.
Why the default is “follow”: Most people copying a file want its contents, not a pointer. The symlink is usually an implementation detail. So
cpfollows by default. When you need the symlink itself, use-Por-a.
A full example
Using cp for common tasks.
# ============================================
# PART 1: BACKUP A FILE
# ============================================
cd ~
echo "important config" > app.conf
cp app.conf app.conf.bak
ls
# app.conf app.conf.bak
# ============================================
# PART 2: COPY TO A DIRECTORY
# ============================================
mkdir backups
cp app.conf backups/
ls backups/
# app.conf
# ============================================
# PART 3: COPY WITH A NEW NAME
# ============================================
cp app.conf backups/app.conf.old
ls backups/
# app.conf app.conf.old
# ============================================
# PART 4: INTERACTIVE OVERWRITE
# ============================================
cp -i app.conf backups/app.conf
# cp: overwrite 'backups/app.conf'? y
# ============================================
# PART 5: NO-CLOBBER
# ============================================
cp -n app.conf backups/app.conf
# (silent, no overwrite)
echo $?
# 0
# ============================================
# PART 6: COPY A DIRECTORY
# ============================================
mkdir -p project/src
touch project/src/main.js
touch project/README.md
cp -r project project-copy
find project-copy
# project-copy
# project-copy/README.md
# project-copy/src
# project-copy/src/main.js
# ============================================
# PART 7: COPY INTO AN EXISTING DIRECTORY
# ============================================
mkdir dest
cp -r project dest/
find dest
# dest
# dest/project
# dest/project/README.md
# dest/project/src
# dest/project/src/main.js
# ============================================
# PART 8: COPY CONTENTS ONLY
# ============================================
mkdir dest2
cp -r project/. dest2/
find dest2
# dest2
# dest2/README.md
# dest2/src
# dest2/src/main.js
# ============================================
# PART 9: PRESERVE TIMESTAMPS
# ============================================
ls -l app.conf
# -rw-r--r-- ... app.conf
sleep 2
cp -p app.conf app-preserved.conf
ls -l app-preserved.conf
# (timestamp same as app.conf)
# ============================================
# PART 10: ARCHIVE COPY
# ============================================
cp -a project project-archive
find project-archive -type f
# project-archive/README.md
# project-archive/src/main.js
# ============================================
# PART 11: COPY SYMLINKS
# ============================================
ln -s app.conf config-link
cp config-link copied-link
ls -l copied-link
# -rw-r--r-- ... copied-link (regular file)
cp -P config-link preserved-link
ls -l preserved-link
# lrwxrwxrwx ... preserved-link -> app.conf
# ============================================
# PART 12: COPY MULTIPLE FILES
# ============================================
touch file1.txt file2.txt file3.txt
mkdir txts
cp *.txt txts/
ls txts/
# file1.txt file2.txt file3.txt
# ============================================
# PART 13: WILDCARD WITH PATTERN
# ============================================
mkdir logs
touch logs/{access,error,debug}.log
cp logs/*.log logs-backup/ 2>/dev/null || true
mkdir logs-backup
cp logs/*.log logs-backup/
ls logs-backup/
# access.log debug.log error.log
Each part demonstrates a different cp use. Together they cover the practical range.
Why this session: It covers backup, directory copying, contents-only copying, timestamp preservation, symlink handling, and wildcards. These are the real tasks
cpdoes. Once they’re familiar,cpis predictable.
Complete Example Session
# ============================================
# PART 1: BASIC FILE COPY
# ============================================
echo "content" > original.txt
cp original.txt copy.txt
cat copy.txt
# [ content ]
ls
# [ copy.txt original.txt ]
# ============================================
# PART 2: COPY TO A DIRECTORY
# ============================================
mkdir archive
cp original.txt archive/
ls archive/
# [ original.txt ]
# ============================================
# PART 3: COPY WITH NEW NAME
# ============================================
cp original.txt archive/newname.txt
ls archive/
# [ newname.txt original.txt ]
# ============================================
# PART 4: MULTIPLE SOURCES
# ============================================
touch a.txt b.txt c.txt
mkdir all
cp a.txt b.txt c.txt all/
ls all/
# [ a.txt b.txt c.txt ]
# ============================================
# PART 5: WILDCARD
# ============================================
touch more.txt
cp *.txt all/
# cp: target 'all' is not a directory? No โ it is
# Actually copies all .txt files
ls all/
# [ a.txt b.txt c.txt more.txt original.txt ]
# ============================================
# PART 6: OVERWRITE DEFAULT
# ============================================
echo "old" > dest.txt
echo "new" > source.txt
cp source.txt dest.txt
cat dest.txt
# [ new ]
# ============================================
# PART 7: -i INTERACTIVE
# ============================================
cp -i source.txt dest.txt
# [ cp: overwrite 'dest.txt'? ]
# Answer n
# cat dest.txt still "new"
# ============================================
# PART 8: -n NO CLOBBER
# ============================================
cp -n source.txt dest.txt
# No prompt, no overwrite
cat dest.txt
# [ new ]
# ============================================
# PART 9: -u UPDATE
# ============================================
# Copy only if source newer
cp -u source.txt dest.txt
cat dest.txt
# [ new ]
# ============================================
# PART 10: RECURSIVE COPY
# ============================================
mkdir -p tree/sub
touch tree/file.txt tree/sub/deep.txt
cp -r tree tree-copy
find tree-copy
# [ tree-copy ]
# [ tree-copy/file.txt ]
# [ tree-copy/sub ]
# [ tree-copy/sub/deep.txt ]
# ============================================
# PART 11: NESTED VS CONTENTS
# ============================================
mkdir existing
cp -r tree existing/
ls existing/
# [ tree ]
mkdir target
cp -r tree/. target/
ls target/
# [ file.txt sub ]
# ============================================
# PART 12: PRESERVE ATTRIBUTES
# ============================================
ls -l original.txt
# [ -rw-r--r-- 1 alice alice 8 Sep 22 10:30 original.txt ]
sleep 2
cp original.txt no-preserve.txt
ls -l no-preserve.txt
# [ -rw-r--r-- 1 alice alice 8 Sep 22 10:32 no-preserve.txt ]
cp -p original.txt preserved.txt
ls -l preserved.txt
# [ -rw-r--r-- 1 alice alice 8 Sep 22 10:30 preserved.txt ]
# ============================================
# PART 13: ARCHIVE COPY
# ============================================
cp -a tree tree-archive
diff -r tree tree-archive
# (no output โ identical)
# ============================================
# PART 14: SYMLINK HANDLING
# ============================================
ln -s original.txt link.txt
ls -l link.txt
# [ lrwxrwxrwx ... link.txt -> original.txt ]
cp link.txt followed.txt
ls -l followed.txt
# [ -rw-r--r-- ... followed.txt ]
file followed.txt
# [ followed.txt: ASCII text ]
cp -P link.txt preserved-link.txt
ls -l preserved-link.txt
# [ lrwxrwxrwx ... preserved-link.txt -> original.txt ]
# ============================================
# PART 15: PRACTICAL BACKUP
# ============================================
mkdir -p ~/backups/$(date +%Y-%m-%d)
cp -a /etc/nginx ~/backups/$(date +%Y-%m-%d)/ 2>/dev/null || echo "nginx not found"
# ============================================
# PART 16: PROJECT TEMPLATE
# ============================================
mkdir -p template/{src,docs,tests}
touch template/README.md
cp -r template newproject
cd newproject
ls
# [ README.md docs src tests ]
Every part exercises a cp feature. Together they cover the practical range.
Quick Reference
cp Syntax
| Form | Meaning |
|---|---|
cp SRC DEST | Copy file to file |
cp SRC DIR/ | Copy file into directory |
cp SRC1 SRC2 DIR/ | Multiple sources into directory |
cp -r DIR1 DIR2 | Copy directory recursively |
cp -r DIR/. DIR2/ | Copy contents into directory |
Common Flags
| Flag | Effect |
|---|---|
-r / -R | Recursive |
-a | Archive โ preserve all, recursive |
-p | Preserve mode, ownership, timestamps |
-i | Interactive โ prompt before overwrite |
-n | No clobber โ don’t overwrite |
-u | Update โ copy only if newer |
-v | Verbose |
-f | Force โ remove and retry if needed |
-l | Hard link instead of copy |
-s | Symlink instead of copy |
-P | No dereference โ copy symlinks |
-L | Always dereference โ follow symlinks |
-d | Same as -P --preserve=links |
-t DIR | Target directory first |
Flags That Imply Others
| Flag | Equivalent to |
|---|---|
-a | -dR --preserve=all |
-d | -P --preserve=links |
-R | Same as -r |
-H | Follow command-line symlinks only |
Destination Rules for Files
| Destination | Result |
|---|---|
| Doesn’t exist | Created with source’s content |
| Existing file | Overwritten (unless -i, -n) |
| Directory | Copied inside with same basename |
Destination Rules for Directories
| Destination | Result |
|---|---|
| Doesn’t exist | New dir with source’s contents |
| Existing dir | Source nested inside |
DIR/. source | Contents copied into destination |
Symlink Behavior
| Flag | Behavior |
|---|---|
| (default) | Follow โ copy target |
-P | No follow โ copy symlink |
-L | Always follow |
-a | Implies -P |
Preserve Options
| Option | Effect |
|---|---|
-p | Preserve mode, ownership, timestamps |
-a | Preserve everything |
--preserve=mode | Mode only |
--preserve=timestamps | Timestamps only |
--preserve=all | Everything |
Error Messages
| Message | Cause |
|---|---|
omitting directory | Used cp without -r on a directory |
No such file or directory | Source doesn’t exist |
Permission denied | No read/write permission |
same file | Source and destination are the same |
target is not a directory | Multiple sources, last isn’t a directory |
cannot overwrite non-directory with directory | Type mismatch |
Common Patterns
| Task | Command |
|---|---|
| Backup file | cp file file.bak |
| Copy to directory | cp file /path/ |
| Copy directory | cp -r dir1 dir2 |
| Copy contents | cp -r dir1/. dir2/ |
| Archive copy | cp -a src dst |
| Interactive | cp -i src dst |
| No overwrite | cp -n src dst |
| Update only | cp -u src dst |
| Preserve meta | cp -p src dst |
| Multiple files | cp file1 file2 dir/ |
When to Use Which Flag
| Situation | Flag |
|---|---|
| Backup everything | -a |
| One file, keep attrs | -p |
| Script safety | -n |
| Confirm each | -i |
| Sync-like | -u |
| Directory | -r |
| Preserve symlinks | -a or -P |
| Verbose | -v |
Interaction with umask
| Action | Effect |
|---|---|
| Default copy | umask applies |
-p | umask ignored, source’s mode kept |
-a | umask ignored |
Verification
| Command | Shows |
|---|---|
diff file1 file2 | Differences |
diff -r dir1 dir2 | Directory diffs |
cmp file1 file2 | Byte comparison |
ls -l | File details |
md5sum file | Hash for equality |
Best Practices
โ Do This:
# Use -i for interactive safety
cp -i source dest # โ
# Use -n in scripts to avoid overwrites
cp -n source dest # โ
# Use -r for directories
cp -r src-dir dest-dir # โ
# Preserve timestamps for backups
cp -p important.conf important.conf.bak # โ
# Use -a for full backups
cp -a project/ backup/ # โ
# Copy contents, not the directory
cp -r source/. destination/ # โ
# Verify after copying
diff -r source/ destination/ # โ
# Use -v in scripts that log
cp -v source dest # โ
# Quote paths with spaces
cp "My File.txt" "My File Copy.txt" # โ
# Use $(date) for timestamped backups
cp app.conf app.conf.$(date +%Y%m%d) # โ
โ Don’t Do This:
# Don't forget -r for directories
cp source-dir dest # โ
# Don't overwrite silently in scripts
cp source dest # overwrites without warning # โ ๏ธ
# Don't assume the destination is what you think
cp file.txt /tmp # is /tmp a dir? yes # โ
# But:
cp file.txt /tmp # if /tmp is a file โ copies over it # โ ๏ธ
# Don't rely on umask for protection
cp secret.txt public/ # umask may not be enough # โ ๏ธ
# Don't use cp for symlinks without -P
cp -r tree copy # loses symlinks # โ ๏ธ
# Don't create content with cp
cp empty.txt file # both empty # โ ๏ธ
# Don't use cp to move large files
cp bigfile bigcopy && rm bigfile # use mv instead # โ ๏ธ
# Don't skip the trailing slash meaning
cp -r src dest/ # nests source inside dest # โ ๏ธ
cp -r src/. dest/ # contents only # โ
# Don't ignore errors
cp -r dir dest 2>/dev/null # silent failure # โ ๏ธ
# Don't copy to a path without permissions
cp file /root/ # permission denied for non-root # โ ๏ธ
Common Pitfalls
| Pitfall | Problem | Solution |
|---|---|---|
Missing -r | Directory copy fails | Add -r |
cp dir dest nests | Unexpected structure | Use dir/. |
| Overwrites silently | Lost file | Use -i or -n |
| Alias not in script | Different behavior | Use full path |
cp -p as non-root | Ownership not preserved | Expected |
cp follows symlinks | Regular file created | Use -P |
| Umask modifies perms | Different from source | Use -p |
| Multiple sources | Last isn’t dir | Fix order |
Source is dir, no -r | Error | Add -r |
| Destination same as source | same file error | Different name |
Real-World Examples
1. Backup a file
cp config.ini config.ini.bak
2. Backup with date
cp config.ini config.ini.$(date +%Y%m%d)
3. Copy to directory
cp report.pdf ~/Documents/
4. Copy with new name
cp original.txt renamed.txt
5. Copy multiple files
cp *.log /var/log/archive/
6. Interactive overwrite
cp -i file1.txt file2.txt
7. No-overwrite copy
cp -n source.txt dest.txt
8. Update-only copy
cp -u /src/* /dest/
9. Copy a directory
cp -r projects projects-backup
10. Copy contents
cp -r template/. myproject/
11. Preserve timestamps
cp -p original.txt preserved.txt
12. Archive copy
cp -a /etc/nginx /backups/nginx-$(date +%F)
13. Copy with symlinks preserved
cp -a node_modules /tmp/nm-backup
14. Verbose copy
cp -rv source-dir dest-dir
15. Force overwrite
cp -f source dest
16. Hard link instead
cp -l original hardlink
17. Symlink instead
cp -s /path/to/file symlink-name
18. Copy a project skeleton
cp -r ~/templates/webapp ./newapp
19. Copy dotfiles
cp -a ~/.config/.* /backup/config/
20. Verify a copy
diff -r source-dir dest-dir
Visual: cp Basics
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ source.txt โ
โ โ content: "hello" โ
โ โ permissions: rw-r--r-- โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โ cp source.txt dest.txt
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ source.txt (unchanged) โ
โ dest.txt (new, identical content) โ
โ โ
โ Both exist now โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Visual: Destination Semantics
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ cp file.txt newname.txt โ
โ โ
โ โ file.txt copied to ./newname.txt โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ cp file.txt /tmp/ โ
โ โ
โ โ file.txt copied to /tmp/file.txt โ
โ โ destination is a directory โ
โ โ source basename is kept โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ cp file.txt existing-file.txt โ
โ โ
โ โ existing-file.txt overwritten โ
โ โ unless -i or -n โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Visual: Directory Copy Behavior
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Case 1: Destination doesn't exist โ
โ โ
โ $ cp -r source dest โ
โ โ
โ source/ โโโบ becomes dest/ โ
โ โ
โ dest/ โ
โ โโโ (source's contents) โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Case 2: Destination exists as a directory โ
โ โ
โ $ cp -r source existing/ โ
โ โ
โ source/ โโโบ nested inside existing/ โ
โ โ
โ existing/ โ
โ โโโ source/ โ
โ โโโ (source's contents) โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Case 3: Copy contents only โ
โ โ
โ $ cp -r source/. existing/ โ
โ โ
โ source's contents โ existing/ โ
โ โ
โ existing/ โ
โ โโโ (source's files) โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Visual: Preserve Attributes
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ $ cp source.txt dest.txt โ
โ โ
โ source.txt: โ
โ mode: 755 โ
โ mtime: Jan 15 09:00 โ
โ โ
โ dest.txt (default cp): โ
โ mode: 755 (from umask? kept) โ
โ mtime: now โ updated โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ $ cp -p source.txt dest.txt โ
โ โ
โ dest.txt: โ
โ mode: 755 (exact) โ
โ mtime: Jan 15 09:00 (preserved) โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ $ cp -a source-dir dest-dir โ
โ โ
โ All of: โ
โ permissions โ
โ
โ ownership (root) โ
โ
โ timestamps โ
โ
โ symlinks as symlinks โ
โ
โ hard links โ
โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Visual: Symlink Behavior
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ source-dir/ โ
โ โโโ file.txt โ
โ โโโ link.txt โ file.txt โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ cp -r source-dir dest โ
โ โ
โ dest/ โ
โ โโโ file.txt โ
โ โโโ link.txt โ regular file (target copy) โ
โ โ
โ (default follows symlinks) โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ cp -a source-dir dest โ
โ โ
โ dest/ โ
โ โโโ file.txt โ
โ โโโ link.txt โ file.txt โ
โ โ
โ (symlink preserved) โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Visual: -i vs -n vs -u
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Default: silent overwrite โ
โ โ
โ $ cp src dst โ
โ (dst overwritten) โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ -i: interactive โ
โ โ
โ $ cp -i src dst โ
โ cp: overwrite 'dst'? _ โ
โ โ
โ y โ overwrite โ
โ n โ skip โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ -n: no clobber โ
โ โ
โ $ cp -n src dst โ
โ (dst not overwritten) โ
โ โ
โ Silent skip โ no prompt โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ -u: update only โ
โ โ
โ $ cp -u src dst โ
โ โ If src newer than dst โ overwrite โ
โ โ If dst newer or same โ skip โ
โ โ If dst missing โ copy โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Visual: Common Backup Workflow
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Before editing a config: โ
โ โ
โ 1. cp /etc/nginx/nginx.conf \ โ
โ /etc/nginx/nginx.conf.bak โ
โ โ
โ 2. (edit /etc/nginx/nginx.conf) โ
โ โ
โ 3. If problems: โ
โ cp /etc/nginx/nginx.conf.bak \ โ
โ /etc/nginx/nginx.conf โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Before upgrading a directory: โ
โ โ
โ 1. cp -a project project-$(date +%F) โ
โ โ
โ 2. (upgrade project) โ
โ โ
โ 3. If problems: โ
โ rm -rf project โ
โ mv project-$(date +%F) project โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Visual: cp vs mv
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ cp source dest โ
โ โ
โ source โ still exists โ
โ dest โ new copy โ
โ โ
โ Reads + writes data โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ mv source dest โ
โ โ
โ source โ gone โ
โ dest โ same inode (if same filesystem) โ
โ โ
โ Renames โ fast if same fs โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Visual: Path Forms
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ cp /etc/hosts /tmp/hosts-copy โ
โ absolute absolute โ
โ โ
โ cp hosts /tmp/ โ
โ relative absolute โ
โ โ
โ cp ~/notes.txt . โ
โ from home to current โ
โ โ
โ cp ../other/file.txt ./file.txt โ
โ from parent to current โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Visual: Decision Flow
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Copying a file? โ
โ โ โ
โ โโโ cp SRC DEST โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Copying a directory? โ
โ โ โ
โ โโโ cp -r SRC DEST โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Want to preserve everything? โ
โ โ โ
โ โโโ cp -a SRC DEST โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Worried about overwriting? โ
โ โ โ
โ โโโ Interactive โโโบ -i โ
โ โโโ Never โโโบ -n โ
โ โโโ Only if newer โโโบ -u โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Copying contents, not the directory? โ
โ โ โ
โ โโโ cp -r src/. dest/ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Preserve symlinks? โ
โ โ โ
โ โโโ cp -a or cp -P โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Summary
| Command | Purpose |
|---|---|
cp SRC DEST | Copy file to file |
cp SRC DIR/ | Copy file into directory |
cp -r DIR1 DIR2 | Copy directory |
cp -r DIR/. DIR2/ | Copy contents only |
cp -a SRC DEST | Archive copy |
cp -p SRC DEST | Preserve attributes |
cp -i SRC DEST | Interactive overwrite |
cp -n SRC DEST | Never overwrite |
cp -u SRC DEST | Copy only if newer |
cp -P SRC DEST | Copy symlinks as symlinks |
Key takeaways:
cpcopies โ the source stays, the destination is new- Destination is a directory โ source copied into it, keeping the basename
- Destination is a name โ the copy becomes that name; existing files are overwritten
-ris required for directories โ no exceptionscp -r src destnests differently depending on whetherdestexistscp -r src/. dest/copies contents, not the directory itself-iprompts before overwrite;-nnever overwrites;-ucopies only if newer-ppreserves permissions, ownership, and timestamps-ais-pplus recursive plus symlink preservation โ the backup flag- Default behavior follows symlinks โ use
-Por-ato preserve them - umask applies to new files unless
-por-ais used - Aliases only affect interactive shells โ scripts see plain
cp - Verify with
diff -r source dest
Remember: cp is the copy command โ source stays, destination is new. It’s simple for files, careful for directories, and full of subtle flags for edge cases. Know the destination semantics (into vs onto), use -r for directories, -a for backups, -i or -n to prevent overwrites, and -p when timestamps matter. Get those right and cp becomes one of the most reliable tools in your kit. Nearly every workflow starts by copying something โ a config for backup, a template for a new project, a file into a directory. That’s what cp is for.
Stop using slow, ad-bloated tool sites! ๐คฎ
๐ Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
โ
Finance (Mortgage, Interest, Inflation)
โ
Tech (Base64, JSON, Dev Suite, IP)
โ
Health (BMI, BMR, TDEE)
โ
Productivity (Timer, Workspace, QR)
โก๏ธ Fast & Private
๐ No data leaves your device
๐ 100% Free
๐ Use it now: https://tools.kandz.me
๐ Bookmark itโyouโll need it later!