You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++复制目录树同步修改时间:Windows权限及文件已存在问题求助

C++目录树复制程序Windows运行故障排查

问题描述

编写的C目录树复制程序可同步最后修改时间,在Linux Ubuntu 22.04 LTS(g/gcc 11.4.0)下正常运行,但在Windows 11(g++/gcc 13.1.0)编译成功后运行失败,出现两类报错:

  • 无法设置文件时间:权限拒绝
  • 无法复制文件:文件已存在
    即使以管理员权限运行CMD/PowerShell,问题仍未解决。

环境

  • Linux Ubuntu 22.04 LTS, g++/gcc 11.4.0
  • Windows 11, g++/gcc 13.1.0

编译命令

  • Linux: g++ -std=c++20 main.cpp -o main
  • Windows: g++ -std=c++20 main.cpp -o main.exe

Windows错误信息

terminate called after throwing an instance of 'std::filesystem::__cxx11::filesystem_error'
what(): filesystem error: cannot set file time: Permission denied [D:\to\working\path\output\test_folder]

terminate called after throwing an instance of 'std::filesystem::__cxx11::filesystem_error'
what(): filesystem error: cannot copy file: File exists [D:\to\working\path\src\test_folder\a1.txt] [D:\to\working\path\output\test_folder\a1.txt]

源代码

#include <iostream>
#include <chrono>
#include <filesystem>

using namespace std;
namespace fs = filesystem;

void copy_directory(const fs::path& src, const fs::path& dst) {
    // directory create
    if (!fs::exists(dst))
        fs::create_directories(dst);

    // directory walk
    for (const auto& entry : fs::directory_iterator(src)) {
        const auto& entry_path = entry.path();
        const auto& file_name = entry_path.filename();
        const auto& dst_file_path = dst / file_name;

        if (fs::is_directory(entry_path)) {
            copy_directory(entry_path, dst_file_path);
        }
        else if (fs::is_regular_file(entry_path)) {
            // files update time to last modification
            auto modify_time = fs::last_write_time(entry_path);
            fs::copy_file(entry_path, dst_file_path, fs::copy_options::overwrite_existing);
            fs::last_write_time(dst_file_path, modify_time);
        }
    }

    // folder update time to last modification
    auto modify_time = fs::last_write_time(src);
    fs::last_write_time(dst, modify_time);
}

int main(int argc, char *argv[]) {
    auto start = chrono::high_resolution_clock::now();
    auto src = argv[1];
    auto dst = fs::current_path() / "output";
    cout << "current_path : " << fs::current_path() << endl;
    cout << "src : " << src << endl;
    cout << "dst : " << dst << endl;

    copy_directory(src, dst);
    auto end = chrono::high_resolution_clock::now();
    auto duration = chrono::duration_cast<chrono::microseconds>(end - start);
    cout << "Time spent: " << duration.count() / 1000000.0 << " seconds" << endl;
    return 0;
}

解决方案

1. 解决"无法复制文件:文件已存在"问题

Linux下fs::copy_options::overwrite_existing可直接覆盖目标文件,但Windows下若目标文件被进程占用(如资源管理器预览、其他程序打开),即使添加该选项也会报错。此外原代码未处理特殊文件/符号链接,也可能引发失败。

修改方式:

方式一:先删除再复制

else if (fs::is_regular_file(entry_path)) {
    auto modify_time = fs::last_write_time(entry_path);
    // 先删除已存在的目标文件
    if (fs::exists(dst_file_path)) {
        fs::remove(dst_file_path);
    }
    fs::copy_file(entry_path, dst_file_path);
    fs::last_write_time(dst_file_path, modify_time);
}

方式二:扩展复制选项

添加更完整的复制规则,覆盖更多场景:

fs::copy_file(entry_path, dst_file_path, 
    fs::copy_options::overwrite_existing | 
    fs::copy_options::copy_symlinks);

2. 解决"无法设置文件时间:权限拒绝"问题

Windows下设置目录修改时间存在两个核心问题:目录可能被系统/其他进程临时锁定;GCC的std::filesystem对Windows目录时间的处理存在兼容性瑕疵。

修复方案:

方案A:重试机制规避临时锁定

添加重试逻辑,避免因临时锁定导致的失败:

// 需要添加<thread>头文件
#include <thread>

// ... 原函数代码 ...

// folder update time to last modification
auto modify_time = fs::last_write_time(src);
bool success = false;
int retry = 3;
while (retry-- > 0 && !success) {
    try {
        fs::last_write_time(dst, modify_time);
        success = true;
    } catch (const fs::filesystem_error& e) {
        this_thread::sleep_for(chrono::milliseconds(100));
    }
}
if (!success) {
    cerr << "Failed to set directory time after retries: " << dst << endl;
}

方案B:调用Windows原生API设置时间

若std::filesystem的接口存在兼容性问题,直接使用Windows APISetFileTime:

#include <windows.h>

void set_directory_last_write_time(const fs::path& dir_path, const fs::file_time_type& modify_time) {
    // 转换时间格式
    auto duration = modify_time.time_since_epoch();
    auto filetime_val = ULARGE_INTEGER{static_cast<ULONGLONG>(chrono::duration_cast<chrono::microseconds>(duration).count())};
    FILETIME ft{};
    ft.dwLowDateTime = filetime_val.LowPart;
    ft.dwHighDateTime = filetime_val.HighPart;

    // 打开目录(需FILE_FLAG_BACKUP_SEMANTICS标记)
    HANDLE hDir = CreateFileW(dir_path.c_str(), GENERIC_WRITE, 
                              FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                              nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
    if (hDir != INVALID_HANDLE_VALUE) {
        SetFileTime(hDir, nullptr, nullptr, &ft);
        CloseHandle(hDir);
    } else {
        cerr << "Failed to open directory: " << dir_path << endl;
    }
}

替换原代码中的fs::last_write_time(dst, modify_time);为:

set_directory_last_write_time(dst, modify_time);

注意:Windows编译时需链接kernel32.lib,编译命令改为:
g++ -std=c++20 main.cpp -o main.exe -lkernel32

3. 额外鲁棒性优化

  • 添加异常捕获:在copy_directory函数中为单个文件/目录操作添加try-catch,避免单个错误导致整个程序终止。
  • 跳过符号链接:Windows下创建符号链接需管理员权限,若无需复制可直接跳过:
if (fs::is_symlink(entry_path)) {
    continue;
}

内容的提问来源于stack exchange,提问作者Dr.Dan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 09:27:32