Complete Guide to Bash cp Command

Introduction to cp

The cp (copy) command is one of the most essential file management commands in Unix/Linux systems. It allows users to copy files and directories from one location to another, with extensive options for preserving attributes, handling symlinks, and controlling the copy behavior. Understanding cp thoroughly is crucial for system administration, backup operations, and daily file management tasks.

Key Concepts

  • Source and Destination: The file(s) to copy and where to copy them
  • File Attributes: Permissions, timestamps, and ownership can be preserved
  • Recursive Copy: Copy entire directory structures
  • Safety Options: Prevent accidental overwrites
  • Preservation: Keep file metadata intact

1. Basic Usage

Simple File Copy

#!/bin/bash
# Copy a single file
cp file.txt file_backup.txt
# Copy file to a different directory
cp document.pdf ~/Documents/
# Copy with same name to directory (note the trailing slash)
cp image.jpg ~/Pictures/
# Copy multiple files to a directory
cp file1.txt file2.txt file3.txt ~/backup/
# Copy with wildcards
cp *.txt ~/documents/
cp file?.log ~/logs/
cp [a-z]*.conf ~/config/
# Copy preserving the directory structure with relative paths
cp ../file.txt ./

Copy with Different Names

#!/bin/bash
# Copy and rename
cp original.txt renamed.txt
cp report.pdf ~/Documents/annual_report_2024.pdf
# Copy with date stamp
cp config.conf "config_$(date +%Y%m%d).conf"
# Copy with version number
cp script.sh script_v2.sh
cp data.csv data_backup.csv
# Batch rename while copying
for file in *.log; do
cp "$file" "backup_$file"
done

2. Essential Options

Common Options Reference

#!/bin/bash
# -i: Interactive (prompt before overwrite)
cp -i important.txt backup/
# -v: Verbose (show what's being done)
cp -v *.txt ~/documents/
# -u: Update (copy only when source is newer or missing)
cp -u source.txt destination.txt
cp -u *.log ~/logs/
# -n: No overwrite (don't overwrite existing files)
cp -n important.txt backup/
# -f: Force (remove destination if needed)
cp -f source.txt destination.txt
# -b: Backup (make backup of existing files)
cp -b config.conf config_backup.conf
# --backup=numbered: Numbered backups
cp --backup=numbered important.txt important.txt
# Creates important.txt.~1~, important.txt.~2~, etc.
# Combine options
cp -iv *.txt ~/backup/      # Interactive and verbose
cp -uv *.log ~/logs/        # Update and verbose
cp -fn source.txt dest.txt  # Force and no overwrite? (contradictory)

Practical Option Combinations

#!/bin/bash
# Safe copy (interactive + verbose)
safe_copy() {
cp -iv "$1" "$2"
}
# Update only (copy newer files)
sync_files() {
cp -uv "$1" "$2"
}
# Backup existing files
backup_copy() {
cp -b --suffix=.bak "$1" "$2"
}
# Force copy with backup
force_backup() {
cp -fb "$1" "$2"
}
# Quiet copy (suppress errors)
quiet_copy() {
cp "$1" "$2" 2>/dev/null || echo "Copy failed"
}
# Copy with progress (using rsync style)
copy_with_progress() {
cp -v "$1" "$2" | pv -l -s $(du -sb "$1" | awk '{print $1}') >/dev/null
}
# Usage examples
safe_copy "important.doc" "backup.doc"
sync_files "new_data.csv" "archive/data.csv"
backup_copy "config.cfg" "config.cfg"

3. Copying Directories

Recursive Copy with -r or -R

#!/bin/bash
# Copy entire directory recursively
cp -r documents/ backup/
# Copy directory contents (with trailing slash)
cp -r documents/* backup/     # Copy contents only
cp -r documents/ backup/      # Copy directory itself
# Copy multiple directories
cp -r dir1 dir2 dir3 ~/backup/
# Copy with preserving symlinks
cp -rL symlinked_dir/ dest/   # Follow symlinks
cp -rP symlinked_dir/ dest/   # Preserve symlinks
# Copy only specific file types recursively
find source_dir -name "*.txt" -exec cp {} dest_dir/ \;
# Using wildcards with directories
cp -r project/src/*.rs project/backup/
cp -r project/*/config/ project/backup/

Advanced Directory Copying

#!/bin/bash
# Copy directory structure without files
find source_dir -type d -exec mkdir -p dest_dir/{} \;
# Copy with exclusions
rsync -av --exclude='*.tmp' --exclude='cache/' source_dir/ dest_dir/
# Copy only hidden files
cp -r source_dir/.* dest_dir/ 2>/dev/null
# Copy directory and preserve all attributes
cp -rp source_dir/ dest_dir/
# Merge directories (copy only missing files)
cp -rn source_dir/* dest_dir/
# Copy directory tree with symbolic links
cp -rP source_dir/ dest_dir/
# Backup entire directory with timestamp
cp -r project/ "project_backup_$(date +%Y%m%d_%H%M%S)/"
# Deep copy (copy contents, not symlinks)
cp -rL source_dir/ dest_dir/
# Copy only new files from directory
find source_dir -newer dest_dir -exec cp {} dest_dir \;

4. Preserving File Attributes

Using -p Option

#!/bin/bash
# Preserve ownership, timestamps, and permissions
cp -p source.txt destination.txt
# Archive mode (recursive + preserve)
cp -a documents/ backup/
# Preserve only specific attributes
# --preserve=mode,ownership,timestamps
cp --preserve=mode source.txt dest.txt
cp --preserve=timestamps source.txt dest.txt
cp --preserve=all source.txt dest.txt
# Copy with same permissions but different ownership
sudo cp --preserve=mode file.txt /target/
# Clone file (preserve everything)
cp -a config.conf config_backup.conf
# Preserve when copying directories
cp -ap source_dir/ dest_dir/
# Example: Backup system files
sudo cp -a /etc/ /etc_backup/
sudo cp -ap /var/log/ /var/log_backup/
# Check preserved attributes
ls -l source.txt dest.txt
stat source.txt dest.txt

Archive Mode (-a)

#!/bin/bash
# Archive mode combines: -r -p -d
# -r: recursive
# -p: preserve attributes
# -d: preserve links
# Create exact copy of directory
cp -a /home/user/documents/ /backup/documents/
# Backup with all attributes
cp -a project/ project_backup/
# Copy while preserving symlinks
cp -a symlinked_dir/ dest_dir/
# System backup example
sudo cp -a /etc/ /etc_bak/
sudo cp -a /home/ /home_bak/
# Application data backup
cp -a /var/www/html/ /backup/www/
# Preserve extended attributes (xattr)
cp -a --preserve=xattr file.txt file_copy.txt
# Preserve context (SELinux)
cp -a --preserve=context file.txt file_copy.txt
# Complete preservation
cp -a --preserve=all source_dir/ dest_dir/

5. Handling Symbolic Links

Link Options

#!/bin/bash
# Create hard link instead of copy
cp -l source.txt link.txt
# Create symbolic link instead of copy
cp -s source.txt symlink.txt
# Follow symlinks (copy what they point to)
cp -L symlink.txt target.txt
# Preserve symlinks (copy the link itself)
cp -P symlink.txt target.txt
# Default behavior: follow symlinks when copying
cp symlink.txt target.txt      # Copies the target file
cp -P symlink.txt target.txt   # Copies the symlink
# Copy directory with symlinks preserved
cp -aP symlinked_dir/ dest_dir/
# Examples
# Create file
echo "content" > original.txt
# Create symlink
ln -s original.txt link.txt
# Copy symlink (follows by default)
cp link.txt copy1.txt  # copy1.txt is a copy of original.txt
# Copy symlink (preserve)
cp -P link.txt copy2.txt  # copy2.txt is a symlink
# Verify
ls -l original.txt link.txt copy1.txt copy2.txt

Complex Link Scenarios

#!/bin/bash
# Copy directory with mixed links
mkdir -p test_dir
echo "original" > test_dir/original.txt
ln -s original.txt test_dir/link1.txt
ln -s /absolute/path test_dir/link2.txt
# Option 1: Follow all links
cp -rL test_dir/ test_dir_followed/
# Option 2: Preserve all links
cp -rP test_dir/ test_dir_preserved/
# Option 3: Archive mode (preserves links)
cp -a test_dir/ test_dir_archive/
# Copy only links (not targets)
find . -type l -exec cp -P {} ../links/ \;
# Update symlinks after copy
cp -a source_dir/ dest_dir/
# Fix absolute symlinks
find dest_dir -type l -lname 'source_dir/*' -exec sh -c '
for link; do
target=$(readlink "$link" | sed "s|source_dir|dest_dir|")
ln -sf "$target" "$link"
done
' sh {} +

6. Advanced Copy Operations

Sparse Files

#!/bin/bash
# Detect sparse files
ls -ls sparse_file.txt
# If blocks < size, it's sparse
# Copy sparse files efficiently
cp --sparse=always sparse_file.txt sparse_copy.txt
cp --sparse=never normal_file.txt normal_copy.txt
cp --sparse=auto file.txt copy.txt  # Default
# Create sparse file for testing
dd if=/dev/zero of=sparse.dat bs=1M seek=100 count=0
# Creates a 100MB file with no actual disk usage
# Copy with sparse detection
cp --sparse=always sparse.dat sparse_copy.dat
ls -ls sparse.dat sparse_copy.dat
# Convert normal file to sparse
cp --sparse=always dense.txt sparse.txt
cp --sparse=auto --reflink=always dense.txt sparse.txt

Reflink (Copy-on-Write)

#!/bin/bash
# Copy-on-write (filesystem support required, e.g., Btrfs, XFS)
cp --reflink=always bigfile.dat bigfile_copy.dat
# No actual data copied until modification
# Auto reflink (use if supported, else normal copy)
cp --reflink=auto bigfile.dat bigfile_copy.dat
# Never use reflink (always copy data)
cp --reflink=never bigfile.dat bigfile_copy.dat
# Check if filesystem supports reflink
cp --reflink=always test.dat test_copy.dat 2>&1 || echo "Not supported"
# Example with large files
fallocate -l 1G bigfile.dat
time cp --reflink=always bigfile.dat copy1.dat  # Instant
time cp --reflink=never bigfile.dat copy2.dat   # Takes time

Selective Copy with Attributes

#!/bin/bash
# Copy only specific attributes
cp --attributes-only source.txt dest.txt
# Copies only metadata, not content
# Preserve only mode
cp --preserve=mode source.txt dest.txt
# Preserve only ownership
sudo cp --preserve=ownership source.txt dest.txt
# Preserve only timestamps
cp --preserve=timestamps source.txt dest.txt
# Preserve xattrs
cp --preserve=xattr source.txt dest.txt
# Copy with specific permissions
cp --no-preserve=mode source.txt dest.txt
chmod 644 dest.txt
# Combine with other operations
cp --preserve=timestamps --sparse=always sparse.dat copy.dat

7. Backup and Versioning

Automatic Backups

#!/bin/bash
# Simple numbered backups
cp --backup=numbered config.conf config.conf
# Creates config.conf.~1~, config.conf.~2~, etc.
# Custom backup suffix
cp --backup --suffix=.bak important.txt important.txt
# Backup with timestamp
cp important.txt "important.txt.$(date +%Y%m%d-%H%M%S)"
# Rotating backups
rotate_backup() {
local file="$1"
local max="${2:-5}"
for i in $(seq $max -1 1); do
if [ -f "$file.$i" ]; then
mv "$file.$i" "$file.$((i+1))" 2>/dev/null
fi
done
cp "$file" "$file.1"
}
# Usage
rotate_backup important.txt 5
# Versioned backups
versioned_backup() {
local file="$1"
local version_dir="versions/$(basename "$file")"
mkdir -p "$version_dir"
cp "$file" "$version_dir/$(date +%Y%m%d_%H%M%S).bak"
}
# Keep only last N backups
cleanup_old_backups() {
local pattern="$1"
local keep="$2"
ls -t $pattern | tail -n +$((keep+1)) | xargs -r rm
}
# Full backup strategy
full_backup() {
local source="$1"
local dest="$2"
local date=$(date +%Y%m%d)
# Full backup
cp -a "$source" "$dest/full_$date"
# Create symlink to latest
ln -sfn "full_$date" "$dest/latest"
}

Incremental Backup Logic

#!/bin/bash
# Simple incremental backup
incremental_backup() {
local source="$1"
local dest="$2"
local state_file="$dest/.backup_state"
if [ ! -f "$state_file" ]; then
# First backup
cp -a "$source" "$dest/full"
find "$source" -type f -exec stat --format='%n %Y' {} \; > "$state_file"
else
# Incremental backup
local inc_dir="$dest/inc_$(date +%Y%m%d_%H%M%S)"
mkdir -p "$inc_dir"
find "$source" -type f | while read file; do
local rel="${file#$source/}"
local mtime=$(stat --format='%Y' "$file")
local last_mtime=$(grep -F "$file" "$state_file" | awk '{print $2}')
if [ "$mtime" != "$last_mtime" ]; then
mkdir -p "$inc_dir/$(dirname "$rel")"
cp -p "$file" "$inc_dir/$rel"
fi
done
# Update state
find "$source" -type f -exec stat --format='%n %Y' {} \; > "$state_file"
fi
}
# Differential backup
differential_backup() {
local source="$1"
local dest="$2"
local base="$dest/base"
local diff="$dest/diff_$(date +%Y%m%d)"
# First, create base if doesn't exist
if [ ! -d "$base" ]; then
cp -a "$source" "$base"
return
fi
# Find changed files
mkdir -p "$diff"
find "$source" -type f | while read file; do
local rel="${file#$source/}"
local base_file="$base/$rel"
if [ ! -f "$base_file" ] || ! cmp -s "$file" "$base_file"; then
mkdir -p "$diff/$(dirname "$rel")"
cp -p "$file" "$diff/$rel"
fi
done
}

8. Performance Optimization

Large File Handling

#!/bin/bash
# Monitor copy progress
copy_with_progress() {
local src="$1"
local dst="$2"
local size=$(stat --format='%s' "$src")
pv -s "$size" "$src" > "$dst"
}
# Parallel file copy
parallel_copy() {
local src_dir="$1"
local dst_dir="$2"
local jobs="${3:-4}"
mkdir -p "$dst_dir"
find "$src_dir" -type f | xargs -P "$jobs" -I {} cp {} "$dst_dir"
}
# Buffer size optimization
dd_copy() {
local src="$1"
local dst="$2"
local bs="${3:-1M}"
dd if="$src" of="$dst" bs="$bs" status=progress
}
# Network-aware copy
network_copy() {
local src="$1"
local dst="$2"
# Compress on the fly
tar czf - "$src" | pv | ssh user@host "tar xzf - -C $dst"
}
# Usage
copy_with_progress "bigfile.iso" "bigfile_copy.iso"
parallel_copy "/data" "/backup" 8
dd_copy "largefile.dat" "largefile_copy.dat" "4M"

Optimization Techniques

#!/bin/bash
# Use rsync for large directories
rsync -av --progress source_dir/ dest_dir/
# Avoid copying unchanged files
rsync -av --checksum source_dir/ dest_dir/
# Compress during transfer
rsync -avz source_dir/ user@host:/dest_dir/
# Limit bandwidth
rsync -av --bwlimit=1000 source_dir/ dest_dir/
# Copy only new files
rsync -av --ignore-existing source_dir/ dest_dir/
# Copy with hard links (space efficient)
cp -al source_dir/ dest_dir/
# Creates hard links, not copies
# Copy with reflinks (COW filesystem)
cp -a --reflink=auto source_dir/ dest_dir/
# Memory-mapped copy for large files
mmap_copy() {
local src="$1"
local dst="$2"
python -c "
import shutil
shutil.copyfile('$src', '$dst')
" 2>/dev/null || cp "$src" "$dst"
}

9. Integration with Other Commands

Piping with cp

#!/bin/bash
# cp doesn't read from stdin directly, but we can use process substitution
# Create file from command output
cp <(ls -la) directory_listing.txt
# Copy from compressed file
zcat archive.gz | cp /dev/stdin extracted_file
# Copy with transformations
sed 's/foo/bar/g' input.txt | cp /dev/stdin output.txt
# Copy multiple files from find
find . -name "*.txt" -exec cp {} /target/ \;
# Copy with grep filtering
grep -l "pattern" *.txt | xargs cp -t /target/
# Copy newest files
find . -type f -mtime -1 -exec cp {} /backup/ \;
# Copy with size limit
find . -type f -size -10M -exec cp {} /small_files/ \;
# Generate and copy
for i in {1..10}; do
echo "Content $i" | cp /dev/stdin "file$i.txt"
done

Xargs Integration

#!/bin/bash
# Copy files with xargs
find . -name "*.txt" -print0 | xargs -0 cp -t /destination/
# Parallel copy with xargs
find . -name "*.jpg" -print0 | xargs -0 -P 4 -I {} cp {} /photos/
# Copy with confirmation
find . -name "*.conf" | xargs -p -I {} cp {} /backup/
# Copy with max arguments
find . -type f | xargs -n 10 cp -t /backup/
# Copy from list file
xargs -a filelist.txt -I {} cp {} /destination/
# Copy with transformation
find . -name "*.txt" | xargs -I {} basename {} | xargs -I {} cp {}.txt /dest/{}.bak
# Safe xargs for filenames with spaces
find . -name "*.pdf" -print0 | xargs -0 -I {} cp "{}" /documents/

10. Error Handling and Safety

Robust Copy Scripts

#!/bin/bash
# Safe copy with error checking
safe_copy() {
local src="$1"
local dst="$2"
# Check source exists
if [ ! -e "$src" ]; then
echo "Error: Source '$src' does not exist" >&2
return 1
fi
# Check if source is readable
if [ ! -r "$src" ]; then
echo "Error: Source '$src' is not readable" >&2
return 1
fi
# Check destination directory is writable
local dst_dir
if [ -d "$dst" ]; then
dst_dir="$dst"
else
dst_dir=$(dirname "$dst")
fi
if [ ! -w "$dst_dir" ]; then
echo "Error: Destination directory '$dst_dir' is not writable" >&2
return 1
fi
# Check for enough space
local src_size=$(du -sb "$src" | cut -f1)
local dst_free=$(df -B1 "$dst_dir" | tail -1 | awk '{print $4}')
if [ "$src_size" -gt "$dst_free" ]; then
echo "Error: Not enough space in destination" >&2
return 1
fi
# Perform copy
if cp "$src" "$dst"; then
echo "Copy successful: $src -> $dst"
return 0
else
echo "Error: Copy failed" >&2
return 1
fi
}
# Copy with verification
copy_with_verify() {
local src="$1"
local dst="$2"
cp "$src" "$dst" || return 1
if cmp -s "$src" "$dst"; then
echo "Verification successful"
return 0
else
echo "Verification failed: files differ" >&2
rm -f "$dst"
return 1
fi
}
# Atomic copy (rename after complete)
atomic_copy() {
local src="$1"
local dst="$2"
local tmp="$dst.tmp.$$"
cp "$src" "$tmp" && mv "$tmp" "$dst"
}
# Retry logic
copy_with_retry() {
local src="$1"
local dst="$2"
local max_attempts="${3:-3}"
local attempt=1
while [ $attempt -le $max_attempts ]; do
if cp "$src" "$dst"; then
return 0
fi
echo "Attempt $attempt failed, retrying..." >&2
sleep $((attempt * 2))
attempt=$((attempt + 1))
done
echo "All $max_attempts attempts failed" >&2
return 1
}

Logging and Monitoring

#!/bin/bash
# Logged copy
log_copy() {
local src="$1"
local dst="$2"
local log_file="${3:-/var/log/copy.log}"
echo "$(date '+%Y-%m-%d %H:%M:%S') - Starting copy: $src -> $dst" >> "$log_file"
if cp "$src" "$dst" >> "$log_file" 2>&1; then
echo "$(date '+%Y-%m-%d %H:%M:%S') - Copy successful" >> "$log_file"
return 0
else
echo "$(date '+%Y-%m-%d %H:%M:%S') - Copy failed" >> "$log_file"
return 1
fi
}
# Progress monitoring
monitor_copy() {
local src="$1"
local dst="$2"
local size=$(stat --format='%s' "$src" 2>/dev/null || echo 0)
# Start copy in background
cp "$src" "$dst" &
local pid=$!
# Monitor progress
while kill -0 $pid 2>/dev/null; do
if [ -f "$dst" ]; then
local copied=$(stat --format='%s' "$dst" 2>/dev/null || echo 0)
if [ "$size" -gt 0 ]; then
local percent=$((copied * 100 / size))
echo -ne "Progress: $percent% ($copied/$size bytes)\r"
fi
fi
sleep 1
done
echo -e "\nCopy complete"
wait $pid
return $?
}
# Email notification on completion
notify_copy() {
local src="$1"
local dst="$2"
local email="$3"
if cp "$src" "$dst"; then
echo "Copy of $src to $dst completed successfully" | mail -s "Copy Success" "$email"
else
echo "Copy of $src to $dst failed" | mail -s "Copy Failed" "$email"
return 1
fi
}

11. Special Use Cases

Copying with Transformations

#!/bin/bash
# Copy and convert line endings (DOS to Unix)
sed 's/\r$//' dos_file.txt > unix_file.txt
# Copy and change permissions
cp source.txt dest.txt && chmod 600 dest.txt
# Copy and compress
cp file.txt <(gzip -c) > file.txt.gz
# Copy and encrypt
cp secret.txt <(gpg -c) > secret.txt.gpg
# Copy and change ownership
sudo cp source.txt /dest/ && sudo chown user:group /dest/source.txt
# Copy and rename with pattern
for file in *.txt; do
cp "$file" "${file%.txt}.bak"
done
# Copy and lowercase filenames
for file in *.[Jj][Pp][Gg]; do
cp "$file" "$(echo "$file" | tr '[:upper:]' '[:lower:]')"
done
# Copy and add prefix
for file in *.txt; do
cp "$file" "backup_$file"
done

Network and Remote Copies

#!/bin/bash
# Copy over SSH
cp file.txt user@remote:/home/user/
# Copy with scp
scp file.txt user@remote:/path/
scp -r directory/ user@remote:/path/
# Copy with rsync
rsync -avz file.txt user@remote:/path/
rsync -avz --progress directory/ user@remote:/path/
# Copy with sftp
echo "put file.txt" | sftp user@remote
# Copy from remote
scp user@remote:/path/file.txt .
# Copy multiple files over SSH
tar czf - *.txt | ssh user@remote "tar xzf - -C /path/"
# Copy with bandwidth limit
scp -l 1000 bigfile.iso user@remote:/path/
# Copy with compression
scp -C largefile.dat user@remote:/path/
# Resume interrupted copy
rsync -avP --partial source.dat user@remote:/path/

Copying Special Files

#!/bin/bash
# Copy device files (requires root)
sudo cp -a /dev/sda /backup/sda.img
# Copy with extended attributes
cp --preserve=xattr file.txt file_copy.txt
# Copy ACLs
getfacl file.txt > file.acl
cp file.txt file_copy.txt
setfacl --restore=file.acl file_copy.txt
# Copy FIFO (named pipe)
cp -a mypipe mypipe_copy
# Copy socket
cp -a /var/run/socket /backup/socket
# Copy proc files (special handling)
cat /proc/cpuinfo > cpuinfo.txt
# Copy with SELinux context
cp --preserve=context file.txt file_copy.txt
# Copy sparse device image
dd if=/dev/sda of=sda.img bs=1M status=progress conv=sparse

12. Script Examples

Directory Synchronization

#!/bin/bash
# Simple directory sync
sync_dirs() {
local src="$1"
local dst="$2"
# Ensure destination exists
mkdir -p "$dst"
# Copy new and modified files
find "$src" -type f | while read file; do
local rel="${file#$src/}"
local target="$dst/$rel"
if [ ! -f "$target" ] || [ "$file" -nt "$target" ]; then
mkdir -p "$(dirname "$target")"
cp -p "$file" "$target"
echo "Updated: $rel"
fi
done
# Remove files not in source
find "$dst" -type f | while read file; do
local rel="${file#$dst/}"
local source="$src/$rel"
if [ ! -f "$source" ]; then
rm -f "$file"
echo "Removed: $rel"
fi
done
}
# Mirror with deletions
mirror() {
local src="$1"
local dst="$2"
# Use rsync for efficient mirroring
rsync -av --delete "$src/" "$dst/"
}
# Two-way sync
bidirectional_sync() {
local dir1="$1"
local dir2="$2"
# Sync dir1 to dir2
rsync -av --delete "$dir1/" "$dir2/"
# Sync dir2 to dir1 (excluding already synced)
rsync -av --delete --ignore-existing "$dir2/" "$dir1/"
}

Backup Script

#!/bin/bash
# Comprehensive backup script
BACKUP_ROOT="/backup"
DATE=$(date +%Y%m%d_%H%M%S)
LOG_FILE="/var/log/backup.log"
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}
backup_files() {
local source="$1"
local name=$(basename "$source")
local dest="$BACKUP_ROOT/${name}_$DATE"
log "Starting backup of $source to $dest"
# Check source
if [ ! -d "$source" ] && [ ! -f "$source" ]; then
log "ERROR: Source $source does not exist"
return 1
fi
# Create destination
mkdir -p "$(dirname "$dest")"
# Perform backup
if cp -a "$source" "$dest"; then
log "Backup successful: $dest"
# Create symlink to latest
local latest="$BACKUP_ROOT/${name}_latest"
rm -f "$latest"
ln -s "$dest" "$latest"
# Verify
if diff -rq "$source" "$dest" >/dev/null 2>&1; then
log "Verification successful"
else
log "WARNING: Verification failed"
fi
# Clean old backups
cleanup_old "$name"
else
log "ERROR: Backup failed"
return 1
fi
}
cleanup_old() {
local name="$1"
local keep=5
cd "$BACKUP_ROOT" || return
ls -t "${name}_"* 2>/dev/null | tail -n +$((keep+1)) | while read old; do
log "Removing old backup: $old"
rm -rf "$old"
done
}
# Usage
backup_files "/home/user/documents"
backup_files "/etc/nginx/nginx.conf"

13. Common Pitfalls and Solutions

Frequent Mistakes

#!/bin/bash
# Mistake 1: Forgetting trailing slash
cp -r /source/dir /dest/dir
# This creates /dest/dir (if exists) or /dest/dir (if not)
# Correct way
cp -r /source/dir/ /dest/dir/  # Copy contents
cp -r /source/dir /dest/        # Copy directory into dest
# Mistake 2: Overwriting files unintentionally
cp source.txt dest.txt  # Overwrites dest.txt without warning
# Safe approach
cp -i source.txt dest.txt  # Prompt before overwrite
cp -n source.txt dest.txt  # No overwrite
# Mistake 3: Not preserving permissions for system files
cp /etc/passwd /tmp/passwd  # Permissions changed
# Better
cp -p /etc/passwd /tmp/passwd
# Mistake 4: Copying symlinks incorrectly
cp symlink.txt copy.txt  # Copies target, not link
# If you want the link
cp -P symlink.txt copy.txt
# Mistake 5: Running out of disk space
cp bigfile.iso /full_disk/
df -h /full_disk  # Check space first
# Better
if [ $(df --output=avail /full_disk | tail -1) -gt $(stat --format='%s' bigfile.iso) ]; then
cp bigfile.iso /full_disk/
else
echo "Not enough space"
fi

Debugging Techniques

#!/bin/bash
# Debug mode
set -x
cp -v source.txt dest.txt
set +x
# Dry run (simulate)
cp -vn source.txt dest.txt  # Verbose + no overwrite
# Trace system calls
strace -e trace=file cp source.txt dest.txt 2>&1 | grep open
# Check what would be copied
find source_dir -type f -name "*.txt" | xargs -I {} echo "cp {} dest_dir/"
# Verify after copy
cmp source.txt dest.txt
diff -r source_dir/ dest_dir/
# Debug permissions
ls -l source.txt dest.txt
# Check file integrity
md5sum source.txt dest.txt
sha256sum source.txt dest.txt
# Monitor filesystem changes during copy
inotifywait -m dest_dir/

14. Best Practices and Tips

Shell Configuration

# ~/.bashrc additions
# Alias for safe copy
alias cp='cp -i'  # Interactive mode
alias cpa='cp -a' # Archive mode
alias cpv='cp -v' # Verbose mode
alias cpn='cp -n' # No overwrite
alias cpb='cp --backup=numbered' # Numbered backups
# Function for copy with progress
cpp() {
local src="$1"
local dst="$2"
if [ -f "$src" ]; then
local size=$(stat --format='%s' "$src")
pv -s "$size" "$src" > "$dst"
else
echo "Source is not a file"
return 1
fi
}
# Copy with timestamp
cpts() {
local src="$1"
local dst="${2:-.}"
local timestamp=$(date +%Y%m%d_%H%M%S)
cp "$src" "$dst/$(basename "$src" .${src##*.})_$timestamp.${src##*.}"
}
# Quick backup
bak() {
cp "$1" "$1.bak"
}
# Restore from backup
restore() {
cp "$1.bak" "$1"
}
# Copy to multiple destinations
cpm() {
local src="$1"
shift
for dst in "$@"; do
cp "$src" "$dst"
done
}

Performance Tips

#!/bin/bash
# 1. Use rsync for large transfers
rsync -av --progress source/ dest/
# 2. Buffer size matters for large files
dd if=source.iso of=dest.iso bs=4M status=progress
# 3. Parallel copy for many small files
find . -name "*.jpg" -print0 | xargs -0 -P 4 -I {} cp {} /dest/
# 4. Copy only changed files
rsync -av --checksum source/ dest/
# 5. Use hard links instead of copy when possible
cp -al source_dir/ dest_dir/
# 6. Avoid copying by using reflinks
cp -a --reflink=auto source_dir/ dest_dir/
# 7. Compress during network copy
tar czf - source_dir/ | ssh user@host "tar xzf - -C dest_dir/"
# 8. Limit I/O priority
ionice -c 3 cp bigfile.iso /dest/
# 9. Nice the process
nice -n 19 cp bigfile.iso /dest/
# 10. Use tmpfs for temporary copies
cp file.txt /dev/shm/
cp /dev/shm/file.txt /final/dest/

Security Best Practices

#!/bin/bash
# 1. Don't copy sensitive files with world-readable permissions
umask 077
cp secret.txt backup/
# 2. Use ACLs for fine-grained permissions
cp -a --preserve=context file.txt file_copy.txt
# 3. Verify file integrity after copy
sha256sum original.txt > original.sha256
cp original.txt copy.txt
sha256sum -c original.sha256 --quiet copy.txt
# 4. Secure deletion of original if needed
cp secret.txt encrypted.txt.gpg && shred -u secret.txt
# 5. Copy with encryption
openssl enc -aes-256-cbc -salt -in secret.txt -out secret.txt.enc
cp secret.txt.enc backup/
# 6. Copy within chroot
sudo chroot /jail cp /outside/file.txt /inside/
# 7. Avoid copying symlinks that point outside sandbox
cp -P symlink.txt dest/ 2>/dev/null || echo "Skipped unsafe symlink"
# 8. Check for setuid bits
find . -perm /6000 -exec cp {} /safe/dir/ \;
chmod -s /safe/dir/*
# 9. Copy with SELinux context
cp --preserve=context source.txt dest.txt
# 10. Log all copy operations
alias cp='cp --debug 2>> /var/log/cp.log'

Conclusion

The cp command is fundamental to Unix/Linux file management, and mastering it involves understanding:

Key Takeaways

  1. Basic Operations: Single files, multiple files, directories
  2. Options: -i (interactive), -v (verbose), -u (update), -n (no overwrite)
  3. Preservation: -p (preserve), -a (archive mode)
  4. Recursive: -r or -R for directories
  5. Links: -L (follow), -P (preserve), -l (hard links), -s (symlinks)
  6. Backup: --backup for versioning
  7. Sparse Files: --sparse for efficient storage
  8. Reflink: --reflink for copy-on-write

Best Practices Summary

  1. Always use -i interactively to prevent accidental overwrites
  2. Use -a for preserving attributes when backing up
  3. Check disk space before large copies
  4. Verify important copies with checksums
  5. Use -n in scripts to prevent overwriting
  6. Be careful with trailing slashes in directory paths
  7. Handle symlinks appropriately for your use case
  8. Use -v to monitor progress or for logging
  9. Consider rsync for complex/large transfers
  10. Test with --dry-run or -n before actual copy

The cp command may seem simple, but its extensive options and combinations make it a powerful tool for any file management task. Understanding these features will make you more efficient and help prevent common mistakes.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper