Copy a directory to an existing directory Linux?


Overview

Copying files is one of the most common operations performed by using the Linux shell. We usually use the cp (copy) commands for this purpose.

We're going to discuss how to recursively move a folder to another location with or without overwriting.

Introduction to the Problem

We first need to understand what "Copy a folder to another location" means in this problem.

A good example can help you understand it better.

$ tree -a
.
├──  src
    ├── .hidden.file
    ├── srcFile.txt
    └── subSrc
        └── subSrcFile.txt
└──  target
     └── originalTarget.file

3 directories, 4 files

Here, as shown in the output above, we have two directories under the src folder. There are some source code file and a sub−folder. We also have a.hidden.file.

We now need to copy the source folder into the destination folder.

We want the destination folder to be identical to the source folder.

├──  src
    ├── .hidden.file
    ├── srcFile.txt
    └── subSrc
        └── subSrcFile.txt
└──  target
     ├── .hidden.file
     ├── srcFile.txt
     └── subSrc
         └── subSrcFile.txt

However, if we just copy the contents of the source folder into the destination folder, we want everything under src to be recursively moved to the destination folder. We also want the original files under the destination to remain unchanged.

├──  src
    ├── .hidden.file
    ├── srcFile.txt
    └── subSrc
        └── subSrcFile.txt
└── target
    ├── .hidden.file
    ├── originalTarget.file
    ├── srcFile.txt
    └── subSrc
        └── subSrcFile.txt

Since the target directory already exist, we cannot just use the cp −r src target method to do this task. Instead, we need to create a new directory called src inside the target directory first. Then, we can move the files from src into the newly created src directory.

After that, we’ll discuss how to achieve our objective by taking two different approaches −

  • Using cp command

  • Using rsync command

Using the cp  Command

Let’s first take a look at how to fix the issue by using the cp command since that is fairly common.

cp Without Overwriting the target Directory

If the source folder is not empty, we cannot simply copy the contents of the source folder into the destination folder using the cp command.

However, cp has an −i option which treats the source as a regular file instead of a folder.

If we use the −rT option, then the cp command will recurse through the contents of the source folder and create copies of them in the target folder.

$ cp -rT src target
$ tree -a
.
├──  src
    ├── .hidden.file
    ├── srcFile.txt
    └── subSrc
        └── subSrcFile.txt
└── target
    ├── .hidden.file
    ├── originalTarget.file
    ├── srcFile.txt
    └── subSrc
        └── subSrcFile.txt

4 directories, 7 files

tree‘s −a option tells us that we’re copying everything from the source directory into the destination directory. However, the source directory isn’t included in the copy operation. So we’re done!

Bash Globbing Trick

We've solved the issue by using the cp command with the −rT option.

We can solve the problem by copying everything from src into target. Therefore, we might be able to write the following: cp −r src/* target.

We can solve the problem by copying everything from src into target. Therefore, we might be able to write the following: cp -r src/* target.

$ cp -r src/* target
$ tree -a
.
├──  src
│    ├── .hidden.file
│    ├── srcFile.txt
│    └── subSrc
│        └── subSrcFile.txt
└── target
    ├── originalTarget.file
    ├── srcFile.txt
    └── subSrc
        └── subSrcFile.txt

4 directories, 6 files

As the tree output indicates, most files under src were successfully transferred to the destination folder, except for the.dot file. This is because by using the ‘globstar’ option, the shell ignores any filename beginning with a period (“.“).

There are two ways to solve this problem. One solution is to modify the global settings so that the globbing includes.dot files. You can use the shopt −s dotglob command for that.

$ ( shopt -s dotglob; cp -r src/* target )
$ tree -a target
target
├──  .hidden.file
├──  originalTarget.file
├──  srcFile.txt
└──  subSrc
     └── subSrcFile.txt

1 directory, 4 files

$ shopt dotglob
dotglob off

We can see from the output above that the dot file has also been successfully created. Also, we wrap our shoptcp commands together with the cp commands in parentheses (…) so they run in a subshell because we want the shoptcp commands to only effect the single cp commands.

After the command executes, when we check the globbing options, they're still disabled.

It's easy enough to change bash's default behavior. But, if you're writing scripts for yourself, it might be inconvenient. Particularly, if you want to avoid unintended consequences.

You could also add.dotfiles to your source control system by using "src/." instead of "src/*".

Let’s take a look at whether it worked as we expected.

$ cp -r src/. target
$ tree -a target
target
├──  .hidden.file
├──  originalTarget.file
├──  srcFile.txt
└──  subSrc
     └── subSrcFile.txt

1 directory, 4 files

cp and Overwrite the target Directory

If we want to replace the contents of the source folder with the contents of the target folder, then it's easier to use the −R option instead of the −O option. So let's first remove the target folder and run a cp −r command.

$ rm -r target && cp -r src target
$ tree -a
.
├──  src
    ├── .hidden.file
    ├── srcFile.txt
    └── subSrc
        └── subSrcFile.txt
└── target
    ├── .hidden.file
    ├── srcFile.txt
    └── subSrc
        └── subSrcFile.txt

4 directories, 6 files

Using the rsync Command

rsync is a useful tool for solving this issue.

rsync Without Overwriting the target Directory

There are two rsync command lines that we can use to copy a directory recursively −

rsync −a source target: copies the contents of the source into target

rsync −a src/ target− copy the contents of the src directory into target

The two commands above are almost identical. The one with the leading slash is exactly what we're looking for.

Let's test it out on our example −

$ rsync -a src/ target
$ tree -a
.
├──  src
    ├── .hidden.file
    ├── srcFile.txt
    └── subSrc
        └── subSrcFile.txt
└── target
    ├── .hidden.file
    ├── originalTarget.file
    ├── srcFile.txt
    └── subSrc
        └── subSrcFile.txt

4 directories, 7 file

The tree’s results show that the command has solved our issue.

rysnc and Overwrite the target Directory

We've learned we can combine two command lines, "sudo rmdir /path/to/folder && sudo cp −r /source/folder /path/to/destination" to solve the problem.

With rsync, we can achieve it all at once −

$ rsync -a --delete src/ target
$ tree -a
.
├──  src
    ├── .hidden.file
    ├── srcFile.txt
    └── subSrc
        └── subSrcFile.txt
└── target
    ├── .hidden.file
    ├── srcFile.txt
    └── subSrc
        └── subSrcFile.txt

4 directories, 6 files

After executing the rsync commands, both target and source contain the same content. We've thus found an alternative solution to our original problem.

The secret to this solution is the −delete switch.

The −delete option tells rsync to delete any file from target/ that isn't present in source/, ensuring that both source and target end up identical at the end of the transfer.

Conclusion

We've discussed how to recursively move a folder into another folder with or without overwriting.

We've learned two ways to solve the problem: by using the common cp (copy) and rsync commands.

We've talked about bash's dotglob setting and how it affects globbing behavior.

Updated on: 26-Dec-2022

684 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements