Docker COPY命令子目录下使用通配符失败问题咨询
1. 是否误解了COPY通配符规则?
是的,你对Docker COPY的通配符解析逻辑存在误解。Docker处理带路径的COPY命令时,会先解析并验证路径中的目录部分是否存在,之后才会对文件名部分应用通配符匹配。
比如COPY src/myfile.tx[t] /,Docker会先检查src目录是否存在于构建上下文中:
- 如果
src目录不存在,直接抛出目录找不到的错误,根本不会处理后面的文件名通配符; - 只有当
src目录存在时,才会在该目录下匹配myfile.tx[t]这个通配符,匹配不到文件时(近期版本)不会报错。
而你测试的COPY myfile.tx[t] /没有报错,是因为当前目录(构建上下文根目录)肯定存在,所以直接处理文件名通配符,匹配不到就忽略。
2. 该行为是Bug还是预期行为?
这是预期行为,并非Bug。Docker的设计逻辑是:COPY命令中的路径部分(除了最后一个通配符匹配的部分)必须是实际存在的目录或文件。当通配符只出现在文件名部分时,前置的目录路径必须存在,否则会触发目录不存在的错误。
你提到的COPY sr[c]/myfile.txt /能正常执行,是因为通配符sr[c]被解析为存在的src目录,目录验证通过后,再处理后面的文件名(此时文件名是明确的myfile.txt),所以逻辑走得通——本质是通配符帮你确认了目录的存在,而非绕过了目录验证规则。
3. 更可靠的解决方案
以下是几种比添加dummy文件或目录名通配符更合理的方案:
方案1:搭配目录下肯定存在的文件一起COPY
如果src目录下有一个必然存在的文件(比如.gitignore、README.md),可以把它和目标通配符一起作为源:
COPY src/.gitignore src/myfile.tx[t] /
这样Docker会先确认src目录存在(因为要复制.gitignore),之后再处理myfile.tx[t]的通配符,匹配不到时会自动忽略,不会报错。
方案2:使用RUN命令配合shell的cp命令
利用shell的通配符处理逻辑,配合错误忽略:
RUN mkdir -p /target && cp -n src/myfile.tx[t] /target/ 2>/dev/null || true
mkdir -p确保目标目录存在;cp -n避免覆盖已存在的文件;2>/dev/null || true将错误输出重定向,即使文件不存在也不会中断构建。
方案3:使用BuildKit的绑定挂载(推荐)
如果你的Docker启用了BuildKit(默认在Docker 20.10+已启用),可以用挂载方式直接访问构建上下文的文件:
RUN --mount=type=bind,source=src,target=/src_build \ cp /src_build/myfile.tx[t] / 2>/dev/null || true
这种方式不需要依赖src目录下的其他文件,直接挂载整个src目录到构建容器中,再用cp处理通配符,灵活性更高。
内容的提问来源于stack exchange,提问作者dansan

