Skip to main content

How to Copy Multiple Files

• 2 min read
bash

Quick Answer: Copy Multiple Files

To copy multiple files in Bash, use cp with wildcards: cp *.txt /destination/. For more control, use a loop: for f in file1 file2 file3; do cp "$f" /destination/; done. For complex selections, combine with find.

Quick Comparison: Multiple File Copying Methods

MethodSyntaxBest ForComplexity
Wildcardscp *.txt dest/Pattern matchingVery simple
for loopfor f in list; do cp ...Explicit listsModerate
find + cpfind . -name ... + cpComplex criteriaModerate
brace expansioncp {file1,file2,file3} dest/Selected filesSimple
xargs + cpfind ... | xargs cpLarge listsAdvanced

Bottom line: Use wildcards for simple patterns, use find for complex criteria.


Copy multiple files efficiently using various methods in Bash. Whether you’re copying specific file types, files matching patterns, or files from a list, understanding different approaches helps you choose the right tool for each situation.

Method 1: Using Wildcards and Glob Patterns

Wildcards provide a quick way to copy multiple files matching a pattern.

# Copy all .txt files to destination
cp *.txt /destination/

# Copy all files in current directory
cp * /destination/

# Copy specific pattern with brace expansion
cp file_{1,2,3}.txt /destination/

# Copy all hidden files
cp .* /destination/

# Copy all files starting with 'log'
cp log* /backup/

# Copy multiple extensions
cp *.{txt,log,csv} /destination/

Example output:

# Before
$ ls -la
file1.txt
file2.txt
file3.txt

$ cp *.txt /destination/

# After
$ ls -la /destination/
file1.txt
file2.txt
file3.txt

Method 2: Copy with Array and Loop

Using an array with a loop provides better control and error handling.

#!/bin/bash

files=("file1.txt" "file2.txt" "file3.txt")
destination="/backup"

for file in "${files[@]}"; do
  if [ -f "$file" ]; then
    cp "$file" "$destination/"
    echo "Copied: $file"
  else
    echo "Warning: File not found - $file"
  fi
done

Output:

Copied: file1.txt
Copied: file2.txt
Copied: file3.txt

Method 3: Copy from List File

Read a file containing a list of files to copy.

#!/bin/bash

list_file="$1"
destination="${2:-.}"

if [ ! -f "$list_file" ]; then
  echo "List file not found: $list_file"
  exit 1
fi

# Read file list and copy
while IFS= read -r file; do
  [ -z "$file" ] && continue  # Skip empty lines
  [[ "$file" =~ ^#.* ]] && continue  # Skip comments

  if [ -f "$file" ]; then
    cp "$file" "$destination/"
    echo "Copied: $file"
  else
    echo "Warning: File not found - $file"
  fi
done < "$list_file"

echo "Copy complete"

Input file (filelist.txt):

./documents/report.pdf
./documents/budget.xlsx
./spreadsheets/sales.csv
# This is a comment
./media/photo.jpg

Output:

Copied: ./documents/report.pdf
Copied: ./documents/budget.xlsx
Copied: ./spreadsheets/sales.csv
Copied: ./media/photo.jpg
Copy complete

Method 4: Using find with -exec

The find command is powerful for complex filtering.

# Find and copy specific files
find . -name "*.log" -exec cp {} /backup/ \;

# More efficiently with xargs
find . -name "*.log" -print0 | xargs -0 cp -t /backup/

# Copy files modified in last 7 days
find . -type f -mtime -7 -exec cp {} /backup/ \;

# Copy files larger than 10MB
find . -type f -size +10M -exec cp {} /backup/ \;

Method 5: Copy with Recursion

Copy files from subdirectories while maintaining structure.

# Copy with directory structure preserved
cp -r /source/directory /destination/

# Copy only specific file types recursively
find /source -type f -name "*.txt" -exec cp {} /destination/ \;

# Copy with tar to preserve permissions and structure
tar -cf - /source/*.txt | tar -xf - -C /destination/

Practical Examples

Example 1: Backup Specific Files

#!/bin/bash

backup_dir="/backup/$(date +%Y%m%d)"
mkdir -p "$backup_dir"

# Define files to backup
files=(
  "/etc/hostname"
  "/etc/hosts"
  "/root/.bashrc"
  "/root/.bash_profile"
)

for file in "${files[@]}"; do
  if [ -f "$file" ]; then
    cp "$file" "$backup_dir/"
    echo "Backed up: $file"
  else
    echo "Warning: $file not found"
  fi
done

echo "Backup complete: $backup_dir"

Output:

Backed up: /etc/hostname
Backed up: /etc/hosts
Backed up: /root/.bashrc
Backed up: /root/.bash_profile
Backup complete: /backup/20241225

Example 2: Copy Recent Logs

#!/bin/bash

# Copy log files modified today
archive_dir="/var/log/archive"
mkdir -p "$archive_dir"

find /var/log -type f -name "*.log" -mtime 0 -exec cp {} "$archive_dir/" \;

echo "Recent logs archived to $archive_dir"

Example 3: Copy with Progress Tracking

#!/bin/bash

source_files=(
  "file1.iso"
  "file2.iso"
  "file3.iso"
)
destination="/external_drive/"
total=${#source_files[@]}
current=0

for file in "${source_files[@]}"; do
  ((current++))

  if [ -f "$file" ]; then
    echo "[$current/$total] Copying: $file"
    cp "$file" "$destination/"
    echo "Success"
  else
    echo "[$current/$total] Error: $file not found"
  fi
done

echo "Copy operation complete: $current/$total files"

Output:

[1/3] Copying: file1.iso
Success
[2/3] Copying: file2.iso
Success
[3/3] Copying: file3.iso
Success
Copy operation complete: 3/3 files

Example 4: Copy with Verification

#!/bin/bash

copy_and_verify() {
  local source="$1"
  local dest="$2"

  if [ ! -f "$source" ]; then
    echo "Error: Source file not found - $source"
    return 1
  fi

  if ! cp "$source" "$dest/"; then
    echo "Error: Copy failed for $source"
    return 1
  fi

  # Verify file size matches
  source_size=$(stat -c%s "$source")
  dest_file="$dest/$(basename "$source")"
  dest_size=$(stat -c%s "$dest_file")

  if [ "$source_size" -eq "$dest_size" ]; then
    echo "Verified: $source"
    return 0
  else
    echo "Error: Size mismatch for $source"
    rm "$dest_file"
    return 1
  fi
}

# Usage
for file in *.zip; do
  copy_and_verify "$file" "/backup/"
done

Example 5: Copy Multiple File Types

#!/bin/bash

# Copy multiple file types with different handling
archives_dir="./archives"
documents_dir="./documents"
images_dir="./images"

mkdir -p "$archives_dir" "$documents_dir" "$images_dir"

# Copy archives
cp *.{zip,tar,gz} "$archives_dir/" 2>/dev/null

# Copy documents
cp *.{pdf,doc,docx,txt} "$documents_dir/" 2>/dev/null

# Copy images
cp *.{jpg,png,gif,bmp} "$images_dir/" 2>/dev/null

echo "Files organized into directories"

Example 6: Selective Copy with Exclusion

#!/bin/bash

source_dir="$1"
dest_dir="$2"

# Copy all except temp files and caches
find "$source_dir" -type f \
  ! -name "*.tmp" \
  ! -name ".cache" \
  ! -path "*/node_modules/*" \
  -exec cp {} "$dest_dir/" \;

echo "Selective copy complete"

Performance Comparison

For copying multiple files:

MethodSpeedMemoryBest For
Wildcard globFastestLowSimple patterns, many files
Array loopVery FastMediumControlled, error handling
find with xargsVery FastLowComplex filtering
find with -execFastLowVery large file counts

Best choice: Use cp *.ext /dest/ for simplicity, find for complex patterns.

Important Considerations

Destination Must Exist

Most copy operations require the destination directory to exist:

# Create destination if needed
mkdir -p /destination/

# Then copy
cp file1.txt file2.txt /destination/

Avoiding Overwriting

Use the -n flag to prevent overwriting:

# Don't overwrite existing files
cp -n *.txt /destination/

Preserving Attributes

Use -p to preserve permissions and timestamps:

cp -p *.txt /destination/

Recursive Copy with Structure

# Preserve directory structure
cp -r /source/* /destination/

Handling Spaces in Filenames

Always quote variables when dealing with spaces:

for file in "${array[@]}"; do
  cp "$file" "$destination/"  # Quotes are important!
done

Key Points

  • Use wildcards for quick simple copies
  • Use loops for control and error handling
  • Use find for complex filtering and large file sets
  • Always create destination directory first
  • Quote variables to handle spaces in filenames
  • Verify copies of critical files
  • Use appropriate flags (-r for recursion, -p for preserve)

Quick Reference

# Copy all txt files
cp *.txt /destination/

# Copy with loop
for file in *.txt; do
  cp "$file" /destination/
done

# Copy from find
find . -name "*.log" -exec cp {} /backup/ \;

# Copy with xargs (efficient)
find . -name "*.txt" | xargs cp -t /destination/

# Copy recursively
cp -r /source/ /destination/

# Copy and preserve attributes
cp -rp /source/ /destination/