Richard
Richard

Reputation: 8758

How to recursively copy files and directories

With C++, is it possible to recursively copy files and directories from one path to another

Considering the following filesystem

src/fileInRoot
src/sub_directory/
src/sub_directory/fileInSubdir

I want to copy

  1. all files and directories or
  2. certain files and directories

from src to another directory target.


I've created a new question since the questions I've found are platform specific and don't include filtering:

Upvotes: 16

Views: 16081

Answers (2)

reggi49
reggi49

Reputation: 560

Roi Danton's method is the best, but consideration that std::filesystem might not be available on all platforms. Maybe this could be an alternative way.

#include <fstream>
#include <filesystem>
#include <iostream>

namespace fs = std::filesystem;

//function copy files
void cpFile(const fs::path & srcPath,
  const fs::path & dstPath) {

  std::ifstream srcFile(srcPath, std::ios::binary);
  std::ofstream dstFile(dstPath, std::ios::binary);

  if (!srcFile || !dstFile) {
    std::cout << "Failed to get the file." << std::endl;
    return;
  }

  dstFile << srcFile.rdbuf();

  srcFile.close();
  dstFile.close();
}

//function create new directory
void cpDirectory(const fs::path & srcPath,
  const fs::path & dstPath) {

  fs::create_directories(dstPath);

  for (const auto & entry: fs::directory_iterator(srcPath)) {
    const fs::path & srcFilePath = entry.path();
    const fs::path & dstFilePath = dstPath / srcFilePath.filename();
    //if directory then create new folder
    if (fs::is_directory(srcFilePath)) {
      cpDirectory(srcFilePath, dstFilePath);
    } else {
      cpFile(srcFilePath, dstFilePath);
    }
  }
}

int main() {
  const fs::path srcPath = root / "src";
  const fs::path dstPath = root / "target";

  // Copy only those files which contain "Sub" in their stem.
  cpDirectory(srcPath, dstPath);

  return 0;

}

Upvotes: 0

Richard
Richard

Reputation: 8758

Yes, it is possible to copy a complete directory structure using nothing else than std C++ ... beginning with C++17 and its std::filesystem which includes std::filesystem::copy.

  1. Copying all files can be done using copy_options::recursive:
// Recursively copies all files and folders from src to target and overwrites existing files in target.
void CopyRecursive(const fs::path& src, const fs::path& target) noexcept
{
    try
    {
        fs::copy(src, target, fs::copy_options::overwrite_existing | fs::copy_options::recursive);
    }
    catch (std::exception& e)
    {
        std::cout << e.what();
    }
}
  1. To copy a certain subset of files using a filter, recursive_directory_iterator can be utilized:
// Recursively copies those files and folders from src to target which matches
// predicate, and overwrites existing files in target.
void CopyRecursive(const fs::path& src, const fs::path& target,
                    const std::function<bool(fs::path)>& predicate /* or use template */) noexcept
{
    try
    {
        for (const auto& dirEntry : fs::recursive_directory_iterator(src))
        {
            const auto& p = dirEntry.path();
            if (predicate(p))
            {
                // Create path in target, if not existing.
                const auto relativeSrc = fs::relative(p, src);
                const auto targetParentPath = target / relativeSrc.parent_path();
                fs::create_directories(targetParentPath);

                // Copy to the targetParentPath which we just created.
                fs::copy(p, targetParentPath, fs::copy_options::overwrite_existing);
            }
        }
    }
    catch (std::exception& e)
    {
        std::cout << e.what();
    }
}

When calling the second method like

#include <filesystem>
#include <iostream>
#include <functional>
namespace fs = std::filesystem;

int main()
{
    const auto root = fs::current_path();
    const auto src = root / "src";
    const auto target = root / "target";

    // Copy only those files which contain "Sub" in their stem.
    const auto filter = [](const fs::path& p) -> bool
    {
        return p.stem().generic_string().find("Sub") != std::string::npos;
    };
    CopyRecursive(src, target, filter);
}

and the given filesystem is in the working directory of the process, then the result is

target/sub_directory/
target/sub_directory/fileInSubdir

You could also pass the copy_options as parameter to CopyRecursive() for even more flexibility.


A list of some of the functions from std::filesystem which were used above:


For production code, I recommend to pull the error handling out of the utility functions. For error handling, std::filesystem provides two methods:

  1. exceptions with std::exception/std::filesystem::filesystem_error
  2. and error codes with std::error_code.

Also take into consideration, that std::filesystem might not be available on all platforms

The filesystem library facilities may be unavailable if a hierarchical file system is not accessible to the implementation, or if it does not provide the necessary capabilities. Some features may not be available if they are not supported by the underlying file system (e.g. the FAT filesystem lacks symbolic links and forbids multiple hardlinks). In those cases, errors must be reported.

Upvotes: 29

Related Questions