在Docker官方文档 https://docs.docker.com/engine/reference/builder/#add
里有这么一段话:
When adding files or directories that contain special characters (such as
[
and]
), you need to escape those paths following the Golang rules to prevent them from being treated as a matching pattern. For example, to add a file namedarr[0].txt
, use the following;
ADD arr[[]0].txt /mydir/
翻译成中文就是:如果添加的文件或目录包含特殊字符(比如 [
和 ]
),需要按照Golang的规则进行转义,以避免被当成匹配模式。
例如,想要添加文件 arr[0].txt
,需要转义为 arr[[]0].txt
。
我测试了一下,确实是这样的,但是这种转义方法实在让人有点费解:
arr[0].txt
arr[[]0].txt
Docker文档里没说清楚到底是怎么转义的,为此我查了一下Golang的文档,才明白是怎么回事。
在Dockerfile里, ADD
指令的
参数里可以包含通配符。比如, *
表示“通配任意多个字符”, ?
表示“通配任意一个字符”。
所以,显然Docker会采用匹配模式的方式来添加文件和目录。我们知道,Docker是用Go语言开发的。查看Go语言的官方文档,其 filepath.Match()
方法定义如下:
func Match(pattern, name string) (matched bool, err error)
其第一个参数“pattern”的定义如下:
pattern:
{ term }
term:
'*' matches any sequence of non-Separator characters
'?' matches any single non-Separator character
'[' [ '^' ] { character-range } ']'
character class (must be non-empty)
c matches character c (c != '*', '?', '\\', '[')
'\\' c matches character c
character-range:
c matches character c (c != '\\', '-', ']')
'\\' c matches character c
lo '-' hi matches character c for lo <= c <= hi
可见, [
和 ]
是特殊字符, []
可以包含一个范围,比如 [1-5]
表示1到5。
下面是一个完整的Go示例:
package main
import (
"fmt"
"path/filepath"
)
func main() {
fmt.Println(filepath.Match("/home/catch[1-5]", "/home/catch2"))
fmt.Println(filepath.Match("/home/catch[1-5]", "/home/catch8"))
}
运行结果如下:
true
false
可见,因为 2
在 [1-5]
范围里,所以返回true,而 8
不在 [1-5]
范围里,所以返回false。
现在,回到Dockerfile。已知源文件名为 arr[0].txt
,如果直接写成:
ADD arr[0].txt dir1/
考一考:实际匹配的源文件名是什么?
答:因为 []
被Docker当作一个范围,而范围里只有一个数字 1
,因此只匹配了 1
,也就是说,实际匹配的源文件名为 arr0.txt
。
如果有兴趣,可以动手试一试,验证一下结果。
显然,需要对 [
和 ]
转义,那么该如何转义呢?
答案:只需用 []
把 [
括起来。
因为 []
被Docker当作一个范围,而范围里只有一个字符 [
,因此只匹配了 [
。后面的内容不需要再转义了。
因此,整个源文件名转义为 arr[[]0].txt
。这就是文章开头提到的,想要添加文件 arr[0].txt
,需要转义为 arr[[]0].txt
。
https://docs.docker.com/engine/reference/builder/#add
https://pkg.go.dev/path/filepath#Match