GitLab CI运行C++程序时切换工作目录行为不符合预期
问题原因
你代码里的工作目录切换逻辑本身有缺陷,和GitLab Runner的配置没有直接关系:
- 全程使用相对路径(
child/、../)做目录切换,路径解析完全依赖程序启动时的初始工作目录,只要启动目录和预期不一致,切换就会失效。你本地能跑通只是因为本地启动程序时刚好处于child的父级目录,属于巧合。 - 没有对
std::filesystem::current_directory的调用结果做任何校验,切换目录失败(比如目标路径不存在)时会直接静默跳过,你完全感知不到错误,后续命令自然会在初始目录执行。 - 你在CI脚本里写的
cd build/tests是shell层面的目录切换,如果你的child目录不是放在build/tests路径下,而是放在项目根目录或者其他位置,程序启动后自然找不到对应的child文件夹,切换直接失败。
解决方案
优先修复代码(根治方案,所有环境通用)
不要用相对路径+../的方式切换目录,全程用绝对路径,加上错误校验,参考实现:
#include <filesystem> #include <stdexcept> #include <cstdlib> namespace fs = std::filesystem; void run_cmd_in_child_dir(const std::string& cmd) { // 先保存启动时的绝对工作目录,不要靠../回退 const fs::path original_workdir = fs::current_path(); try { // 拼接目标目录的绝对路径,消除相对路径解析歧义 const fs::path target_workdir = original_workdir / "child"; // 先校验目标路径是否存在 if (!fs::is_directory(target_workdir)) { throw std::runtime_error("Target directory not found: " + target_workdir.string()); } // 切换目录 fs::current_path(target_workdir); // 执行命令 int exit_code = std::system(cmd.c_str()); if (exit_code != 0) { throw std::runtime_error("Command exit with code: " + std::to_string(exit_code)); } } catch (...) { // 无论成功失败,强制切回原工作目录 fs::current_path(original_workdir); throw; } fs::current_path(original_workdir); }
如果你的child目录是和可执行文件放在同一路径,而不是和启动目录绑定,那先拿到可执行文件的绝对路径再拼接:
fs::path exe_path = fs::read_symlink("/proc/self/exe"); fs::path target_workdir = exe_path.parent_path() / "child";
这种写法完全不依赖启动时的工作目录,不管是本地、CI还是其他部署环境都能正常运行。
CI配置临时适配(仅临时验证用,不推荐长期用)
如果暂时不想改代码,可以在CI脚本里先确认路径,保证程序启动时的工作目录和你本地完全一致:
testing: stage: testing image: myhub/archlinux script: - export BOOST_INCLUDEDIR="/usr/include/boost/" - export BOOST_LIBRARYDIR="/usr/lib/" - cd $CI_PROJECT_DIR/build/tests - pwd # 打印当前路径,确认和本地启动路径一致 - ls -la # 打印当前目录文件,确认child文件夹确实存在 - ./buildUStests
绝大多数这类CI路径问题,加了pwd和ls -la之后立刻就能定位到——基本都是实际目录结构和你预期的不一致,导致切换目录的调用失败。
内容的提问来源于stack exchange,提问作者Scary Monsters
相关产品推荐
相关产品推荐

