DEBUG.WIKI
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国
雨云;给项目一台云服务器
RackNerd;$21.99/年 · 1GB KVM

curl 加了 --create-dirs,保存响应头仍报 Failed to open

curl 早于 8.11.0 时,--create-dirs 不会为响应头文件创建目录。区分本地写入失败、输出路径与版本边界,并给出兼容旧版本的目录准备方法。

给 curl 指定响应头文件,例如 --dump-header ./capture/headers.txt,同时加上 --create-dirs,却仍然看到:

Warning: Failed to open ./capture/headers.txt
curl: (23) Failed writing received data to disk/application

如果 capture 目录尚不存在,先核对 curl 的实际版本。从 curl 8.11.0 开始,--create-dirs 才会为 --dump-header 指定的文件创建缺失目录。 在较早版本中,它能为正文输出创建目录,不代表响应头文件也享有同样的处理。

这条版本边界只解释“响应头父目录不存在”这一场景。错误码 23 还可能来自其他本地写入失败,不能仅凭这个数字就断定需要升级。

确认运行的是哪个 curl

在执行下载脚本的同一个 shell 或容器中查看:

command -v curl
curl --version
pwd
ls -ld ./capture

curl --version 第一行显示命令行程序版本。宿主机已经升级,不意味着容器镜像或定时任务使用的 curl 也已升级。pwd 用来确认相对路径的起点;脚本从另一个目录运行时,./capture 指向的地方也会改变。

若最后一条命令显示目录不存在,且实际 curl 早于 8.11.0,就符合本文讨论的条件。发行版可能回移补丁,版本号是判断线索;先创建父目录的办法不依赖该功能是否已回移。

如果目录已经存在,就检查报错里的完整文件路径是否与预期一致,以及执行账户是否能进入目录、创建或覆盖目标文件。只读挂载、磁盘空间不足、已有文件不可写也不会因添加 --create-dirs 而消失。此时应根据本地写入条件继续排查,不要把所有错误码 23 都归到版本问题。

兼容旧版本:请求前创建父目录

对于不能立即更换 curl 的脚本,可以把创建目录作为明确的前置步骤。下面是 POSIX shell 示例;将 URL 换成自己的目标,输出目录使用新的专用目录,避免覆盖已有记录:

capture_dir='./capture-request-001'
mkdir -p "$capture_dir" &&
curl --dump-header "$capture_dir/headers.txt" \
     --output "$capture_dir/body.html" \
     'https://example.com/'

这里 mkdir -p 负责创建目录,&& 保证目录创建成功后才发出请求。响应头和正文使用同一个明确的父目录,旧版本 curl 不需要再替响应头创建目录。

如果原来的脚本按下载 ID 分目录,就在每次请求之前创建对应目录;不要依赖正文写入时才创建目录。对于 --config 中组织的大量请求,可以由生成配置的脚本先创建需要的目录,再交给 curl 执行。目录名来自外部输入时,应限制为自己的下载目录下的合法名称,避免把路径分隔符或 .. 原样带入。

这个例子发送普通 GET 请求并保存正文。若实际需要的是 HEAD 请求,应按接口支持情况保留原有 --head;增加目录创建步骤不要求改变请求方法、认证或重定向策略。

curl 8.11.0 及以上可直接创建响应头目录

确认实际执行程序支持该功能后,可以使用:

curl --create-dirs \
     --dump-header './capture-request-002/headers.txt' \
     --output './capture-request-002/body.html' \
     'https://example.com/'

它会尝试为响应头文件补齐缺失的目录组件。目录创建仍受当前账户权限和文件系统状态限制;它不会把只读挂载改成可写,也不会绕过目录访问权限。

还有一个容易混淆的选项是 --output-dir:它为 --output 或 --remote-name 的正文文件指定输出目录。不要因为设置了它,就假定 --dump-header headers.txt 会自动落到同一个目录。响应头路径应单独写清楚,如上面的 ./capture-request-002/headers.txt。

检查文件内容,并保留兼容写法

请求结束后,先检查 curl 的退出状态,再查看指定位置的两个文件。退出码不为 0 时不能把残留文件当成完整结果;响应头文件即使存在,也可能为空。打开头文件,确认它确实包含本次目标请求的 HTTP 状态行和响应字段,再核对正文是否符合预期。

目录创建成功只解决本地保存问题。服务器返回的 HTTP 错误与本地写入失败是不同检查项,应继续沿用脚本原有的 HTTP 状态判断规则。

响应头可能含有 Set-Cookie 等敏感信息,正文也可能包含业务数据。不要把完整文件直接贴到公开日志或问题讨论中。使用当前账户可写的专用目录即可,不要为了消除报错改用 sudo curl 或把目录权限放宽到所有人可写。

如果升级后需要回到原有运行环境,可以继续保留“先 mkdir -p,再执行 curl”的写法。它兼容旧版本,也让目录准备失败的位置更明确。清理时仅删除已经确认不再需要的本次捕获文件;不要为这类错误清空整个下载目录。