std::filesystem::copy(3)

πŸ“¦ version 2024.07.05, in nixpkgs unstable, opensuse-leap 16.0

πŸ’– NAME πŸ’–

std::filesystem::copy β€” std::filesystem::copy πŸ’… okay so this is the std::filesystem::copy(3) page from the C++ Standard Libary (yes, the header spells it like that, not me 😭), and it is literally the manual for copying files and directories with a whole variety of options.


✨ Synopsis ✨

Defined in header <filesystem>:

void copy( const std::filesystem::path& from,
           const std::filesystem::path& to );                       (1) (since C++17)
void copy( const std::filesystem::path& from,
           const std::filesystem::path& to,
           std::error_code& ec );                                   (2) (since C++17)
void copy( const std::filesystem::path& from,
           const std::filesystem::path& to,
           std::filesystem::copy_options options );                 (3) (since C++17)
void copy( const std::filesystem::path& from,
           const std::filesystem::path& to,
           std::filesystem::copy_options options,
           std::error_code& ec );                                   (4) (since C++17)

Copies files and directories, with a variety of options. Iconic.

1,2) The default, equivalent to (3,4) with copy_options::none used as options.

3,4) Copies the file or directory from to file or directory to, using the copy options indicated by options.

⚠️ BESTIE, HEAR ME: the behavior is undefined if there is more than one option in any of the copy_options option group present in options (even in the copy_file group). Do not do that. For real. 😭

The behavior is as follows:

  • First, before doing anything else, obtains type and permissions of from by no more than a single call to:
    • std::filesystem::symlink_status, if copy_options::skip_symlinks, copy_options::copy_symlinks, or copy_options::create_symlinks is present in options;
    • std::filesystem::status otherwise.
  • If necessary, obtains the status of to, by no more than a single call to:
    • std::filesystem::symlink_status, if copy_options::skip_symlinks or copy_options::create_symlinks is present in options;
    • std::filesystem::status otherwise (including the case where copy_options::copy_symlinks is present in options).
  • If either from or to has an implementation-defined file type, the effects of this function are implementation-defined.
  • If from does not exist, reports an error.
  • If from and to are the same file as determined by std::filesystem::equivalent, reports an error.
  • If either from or to is not a regular file, a directory, or a symlink, as determined by std::filesystem::is_other, reports an error.
  • If from is a directory, but to is a regular file, reports an error.
  • If from is a symbolic link, then:
    • If copy_options::skip_symlink is present in options, does nothing.
    • Otherwise, if to does not exist and copy_options::copy_symlinks is present in options, then behaves as if copy_symlink(from, to).
    • Otherwise, reports an error.
  • Otherwise, if from is a regular file, then:
    • If copy_options::directories_only is present in options, does nothing.
    • Otherwise, if copy_options::create_symlinks is present in options, creates a symlink to to. Note: from must be an absolute path unless to is in the current directory.
    • Otherwise, if copy_options::create_hard_links is present in options, creates a hard link to to.
    • Otherwise, if to is a directory, then behaves as if copy_file(from, to/from.filename(), options) (creates a copy of from as a file in the directory to).
    • Otherwise, behaves as if copy_file(from, to, options) (copies the file).
  • Otherwise, if from is a directory and copy_options::create_symlinks is set in options, reports an error with an error code equal to std::make_error_code(std::errc::is_a_directory).
  • Otherwise, if from is a directory and either options has copy_options::recursive or is copy_options::none:
    • If to does not exist, first executes create_directory(to, from) (creates the new directory with a copy of the old directory's attributes).
    • Then, whether to already existed or was just created, iterates over the files contained in from as if by for (const std::filesystem::directory_entry& x : std::filesystem::directory_iterator(from)) and for each directory entry, recursively calls copy(x.path(), to/x.path().filename(), options | in-recursive-copy), where in-recursive-copy is a special bit that has no other effect when set in options. (The sole purpose of setting this bit is to prevent recursive copying subdirectories if options is copy_options::none.)
  • Otherwise does nothing.

πŸŽ€ Parameters πŸŽ€

  • from β€” path to the source file, directory, or symlink.
  • to β€” path to the target file, directory, or symlink.
  • ec β€” out-parameter for error reporting in the non-throwing overload.

🫢 Return value 🫢

(none) β€” she returns nothing, bestie. Void vibes only. ✨


πŸ’€ Exceptions πŸ’€

  • Any overload not marked noexcept may throw std::bad_alloc if memory allocation fails.
  • 1,3) Throws std::filesystem::filesystem_error on underlying OS API errors, constructed with from as the first path argument, to as the second path argument, and the OS error code as the error code argument.
  • 2,4) Sets a std::error_code& parameter to the OS API error code if an OS API call fails, and executes ec.clear() if no errors occur.

🌸 Notes 🌸

The default behavior when copying directories is the non-recursive copy: the files are copied, but not the subdirectories:

// Given
// /dir1 contains /dir1/file1, /dir1/file2, /dir1/dir2
// and /dir1/dir2 contains /dir1/dir2/file3
// After
std::filesystem::copy("/dir1", "/dir3");
// /dir3 is created (with the attributes of /dir1)
// /dir1/file1 is copied to /dir3/file1
// /dir1/file2 is copied to /dir3/file2

While with copy_options::recursive, the subdirectories are also copied, with their content, recursively.

// ...but after
std::filesystem::copy("/dir1", "/dir3",
                      std::filesystem::copy_options::recursive);
// /dir3 is created (with the attributes of /dir1)
// /dir1/file1 is copied to /dir3/file1
// /dir1/file2 is copied to /dir3/file2
// /dir3/dir2 is created (with the attributes of /dir1/dir2)
// /dir1/dir2/file3 is copied to /dir3/dir2/file3

πŸ¦‹ Example πŸ¦‹

// Run this code

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

int main()
{
    fs::create_directories("sandbox/dir/subdir");
    std::ofstream("sandbox/file1.txt").put('a');
    fs::copy("sandbox/file1.txt", "sandbox/file2.txt"); // copy file
    fs::copy("sandbox/dir", "sandbox/dir2"); // copy directory (non-recursive)
    const auto copyOptions = fs::copy_options::update_existing
                           | fs::copy_options::recursive
                           | fs::copy_options::directories_only
                           ;
    fs::copy("sandbox", "sandbox_copy", copyOptions);
    static_cast<void>(std::system("tree"));
    fs::remove_all("sandbox");
    fs::remove_all("sandbox_copy");
}

🌈 Possible output: 🌈

.
β”œβ”€β”€ sandbox
β”‚   β”œβ”€β”€ dir
β”‚   β”‚   └── subdir
β”‚   β”œβ”€β”€ dir2
β”‚   β”œβ”€β”€ file1.txt
β”‚   └── file2.txt
└── sandbox_copy
    β”œβ”€β”€ dir
    β”‚   └── subdir
    └── dir2

8 directories, 2 files

🧚 Defect reports 🧚

The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

  • LWG 3013, applied to C++17: error_code overload marked noexcept but can allocate memory β†’ correct behavior: noexcept removed.
  • LWG 2682, applied to C++17: attempting to create a symlink for a directory succeeds but does nothing β†’ correct behavior: reports an error.

πŸ‘‘ See also πŸ‘‘

  • copy_options β€” specifies semantics of copy operations (C++17) (enum)
  • copy_symlink β€” copies a symbolic link (C++17) (function)
  • copy_file β€” copies file contents (C++17) (function)

That’s the whole page, bestie 🫢 source/version: http://cppreference.com; date: 2024.06.10. Mwah πŸ’‹βœ¨

πŸ“¦ where this page comes from