Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to recursively copy files and directories

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

  • without having to use any additional libraries?
  • and with platform independent functions?

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:

  • C++ Copy directory recursive under unix (unix)
  • Copy directory content (linux)
  • C/C++ Copy file with automatic recursive folder/directory creation (winapi)
like image 529
Roi Danton Avatar asked Jul 19 '18 20:07

Roi Danton


1 Answers

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:

  • relative() substracts the paths and heeds symlinks (it uses path::lexically_relative() and weakly_canonical())
  • create_directories() creates a directory for every element of the given path that does not already exist
  • current_path() returns (or changes) the absolute path of the current working directory
  • path::stem() returns the filename without the final extension
  • path::generic_string() gives the narrow string with platform independent directory separator /

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.

like image 196
Roi Danton Avatar answered Sep 28 '22 10:09

Roi Danton