How to Copy Files and Directories in Bash

How to Copy Files and Directories in Bash

How to Copy Files and Directories in Bash

Copying files feels like it should be the simplest thing in the world, and most of the time it is — until you’re trying to preserve permissions during a server migration, or you accidentally overwrite a file you needed, or you’re copying thousands of files and want progress feedback. I’ve run into all of these situations, and the cp command has more depth to it than most people realize on their first pass.

This guide covers everything from basic file copying to preserving metadata, copying directories recursively, handling conflicts safely, and automating copy operations in scripts.

The Basic cp Command

cp source.txt destination.txt

This copies source.txt to a new file called destination.txt in the same directory. If destination.txt already exists, it gets overwritten without warning by default — something to keep in mind.

Copying a File Into a Directory

cp report.txt /home/user/documents/

This copies report.txt into the documents directory, keeping the original filename.

Copying Multiple Files at Once

cp file1.txt file2.txt file3.txt /home/user/backup/

When copying multiple files, the last argument must be a directory, since cp needs somewhere to put all of them.

Copying Directories Recursively

By default, cp refuses to copy directories unless you tell it to do so recursively.

cp -r source_folder/ destination_folder/

If destination_folder doesn’t exist, it will be created and populated with the contents of source_folder. If destination_folder already exists, source_folder gets copied into it as a subdirectory — this trips people up constantly, so always double-check the resulting structure with ls afterward.

Preserving File Attributes

By default, copying a file can change its timestamps and sometimes its permissions depending on your system’s umask settings. To preserve the original attributes:

cp -p source.txt destination.txt

For a complete, archive-style copy that also preserves symbolic links and directory structure:

cp -a source_folder/ destination_folder/

Preventing Accidental Overwrites

cp -i source.txt destination.txt

I actually alias cp to always include -i (as covered in the aliases article) since it’s such a cheap safety net against accidental data loss.

To never overwrite existing files instead of prompting:

cp -n source.txt destination.txt

Verbose Output

cp -v source.txt destination.txt

Copying With Wildcards

cp *.txt /home/user/documents/

This copies every file ending in .txt in the current directory into the documents folder.

cp report_*.csv /home/user/reports/

This copies only files matching the pattern report_*.csv, useful for selectively copying related files without grabbing everything in a directory.

Copying Only If Source Is Newer

cp -u source.txt destination.txt

Copying Between Remote Machines

While plain cp only works locally, scp (secure copy) uses the same underlying logic for copying files over SSH to remote systems.

scp report.txt user@remote-server.com:/home/user/documents/

To copy a directory recursively over SSH:

scp -r project_folder/ user@remote-server.com:/home/user/projects/

For syncing large directories or making repeated transfers efficient (only copying changed files), rsync is generally the better tool:

rsync -avz project_folder/ user@remote-server.com:/home/user/projects/

Breaking down these flags:

Copying With Progress Feedback

For large files, plain cp gives no progress indicator, which can be frustrating. One common trick uses rsync instead, even for local copies, because it supports a progress flag:

rsync -ah --progress bigfile.iso /home/user/backup/

Real-World Use Cases

Server migrations: Using cp -a to preserve exact permissions and ownership when moving a website’s files to a new hosting environment.

Backup before editing: A quick habit of running cp important_config.conf important_config.conf.bak before making risky changes, so you can always roll back.

Deploying static files: Copying built assets (like a compiled frontend) from a build directory into a web server’s public directory as part of a deployment script.

Batch duplication for testing: Copying a database dump multiple times with different names to set up isolated test environments.

Automation Example: Timestamped Backup Copy

#!/bin/bash

SOURCE_FILE="/etc/nginx/nginx.conf"
BACKUP_DIR="/etc/nginx/backups"
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")

mkdir -p "$BACKUP_DIR"
cp -p "$SOURCE_FILE" "$BACKUP_DIR/nginx.conf.$TIMESTAMP.bak"

echo "Backup saved as $BACKUP_DIR/nginx.conf.$TIMESTAMP.bak"

How this works internally:

  1. date +"%Y%m%d_%H%M%S" generates a unique timestamp so repeated runs don’t overwrite previous backups.
  2. mkdir -p ensures the backup directory exists without erroring if it’s already there.
  3. cp -p copies the config file while preserving its original permissions and timestamps, which matters for configuration files where permission mistakes can break a service.
  4. This pattern is common before editing critical configuration files on production servers, giving you an instant rollback option if something goes wrong.

Best Practices

Security Considerations

Optimization Tips

Troubleshooting Common Issues

“cp: omitting directory” error: You forgot the -r flag when trying to copy a directory.

Copied directory ends up nested one level deeper than expected: This happens when the destination directory already exists — cp -r source/ dest/ copies source into dest rather than merging its contents, if dest already exists.

Permissions look different after copying: Use -p or -a to preserve original permissions; without them, cp applies your current umask to new files.

“No space left on device” during copy: Check available disk space with df -h before large copy operations, especially when duplicating big files or directories.

Frequently Asked Questions

What’s the difference between cp -r and cp -a? -r only makes the copy recursive; -a additionally preserves permissions, timestamps, ownership, and symbolic links, making it a true “archive” copy.

Does cp follow symbolic links by default? Yes, by default cp copies the file a symlink points to, not the symlink itself. Use -P to copy the symlink itself instead of following it, or -a which preserves symlinks appropriately.

How do I copy only specific file types from a directory? Use wildcards, like cp source_dir/*.jpg destination_dir/, or combine with find -exec cp {} destination_dir/ \; for more complex filtering.

Is rsync always better than cp? Not always — for simple, one-off local copies, cp is simpler and perfectly adequate. rsync shines for large transfers, repeated syncs, and remote copying where efficiency matters.

Common Mistakes to Avoid

Summary

Copying files and directories in Bash starts with the simple cp command but has real depth once you factor in recursive copying, permission preservation, conflict handling, and remote transfers. Knowing when to reach for -a versus -r, when to use -i for safety, and when to switch to rsync for larger or repeated transfers will make your file management both safer and more efficient.

References

Exit mobile version