> For the complete documentation index, see [llms.txt](https://camelgemonion.gitbook.io/docker/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://camelgemonion.gitbook.io/docker/dockerfile-zhi-ling/add.md).

# ADD

## ADD

**ADD有两种形式：**

```
ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]
```

对于包含空格的路径，需要使用后一种形式。

**NOTE:**

**--chown**功能仅在用于构建**Linux**容器的**Dockerfiles**上受到支持，在**Windows**容器上不起作用。由于**用户和组所有权**概念无法在**Linux**和**Windows**之间进行转换，因此使&#x7528;**/etc/passwd**&#x548C;**/etc/group**将**用户名和组名**转换为**ID**限制了该功能，使得该功能仅适用于基于**Linux OS**的容器。

**ADD指令**从复制新文件，目录或远程文件**URL**，并将它们添加到镜像文件系统中的路径中。

每个都可以包含通配符，并且匹配根据**Go**的[**filepath.Match**](https://golang.org/pkg/path/filepath/#Match)规则进行。例如：

要添加所有&#x4EE5;**"hom"**&#x5F00;头的文件：

```
ADD hom* /mydir/
```

在下面的示例中，**?**&#x53EF;以被替换为任何单个字符，例&#x5982;**"home.txt"**。

```
ADD hom?.txt /mydir/
```

是绝对路径，或相对于**WORKDIR**的路径，源文件将被复制到目标容器所在文件系统的路径中。

下面的示例使用相对路径，并&#x5C06;**"test.txt“**&#x6DFB;加到 **/relativeDir/**：

```
ADD test.txt relativeDir/
```

而此示例使用了绝对路径，并&#x5411;**/absoluteDir/**&#x6DFB;加&#x4E86;**"test.txt"**

```
ADD test.txt /absoluteDir/
```

在添加包含特殊字符的文件或目录（如 **\[和]**） 时，需要按照**Golang**规则对这些路径进行转义，以防止它们被视为匹配模式。例如，要添加名为**arr\[0].txt**的文件，请使用以下文件：

```
ADD arr[[]0].txt /mydir/
```

**附go代码示例**：

```go
函数声明:
func Match(pattern, name string) (matched bool, err error)

函数作用:
如果name匹配shell文件名模式匹配字符串，Match函数返回真（注意：Match要求匹配整个name字符串，不是它的一部分。如果pattern语法错误时，会返回"syntax error in pattern"的错误）

可用的匹配字符如下:

    '*'                                  匹配0或多个非/的字符
    '?'                                  匹配1个非/的字符
    '[' [ '^' ] { character-range } ']'  字符组（必须非空）(支持三种格式[abc],[^abc],[a-c])
    c                                    匹配字符c（c != '*', '?', '\\', '['）
    '\\' c                               匹配字符c(可上面c的区别是 可以支持字符 * ? \\ [的匹配)

测试实例:
* 字符测试实例

    //* 匹配0或多个非/的字符
    path.Match("*", "a")            // true    
    path.Match("*", "sefesfe/")     // false
? 字符测试实例

    //？匹配一个非/的字符
    path.Match("a?b", "aab")    // true
    path.Match("a?b", "a/b")    // false
[] 格式测试实例

    path.Match("[abc][123]", "b2")        // true

    path.Match("[abc][1-3]", "b2")        // true

    path.Match("[abc][^123]", "b2")        // false

    path.Match("[abc][^123]", "b4")        // true
字符或者特殊用途字符(  \\   ?  *   [ )测试实例

    path.Match("a\\\\", "a\\")    // true
    path.Match("a\\[", "a[")        // true
    path.Match("a\\?", "a?")        // true
    path.Match("a\\*", "a*")        // true
```

所有新文件和目录均以值为**0**的**UID**和**GID**创建，除非通过可选&#x7684;**--chown**指定**用户名**、**组名**或**UID/GID**组合，来为添加的内容指定**所有权(ownership)**。**--chown**允许使用**用户名**和**组名字符串**的格式，或直接使用**整数形式**的**UID**和**GID**。如果使用不带**组名**的**用户名**或不带**GID**的**UID**的形式，则会使用与**UID**相同的值来作为**GID**。如果提供了**用户名**或**组名**，则会根据容器根文件系统中&#x7684;**/etc/passwd**&#x548C;**/etc/group**文件来将**用户名**和**组名**从**name**的形式转换为**整数**的形式。以下示例显示&#x4E86;**--chown**的有效定义：

```
ADD --chown=55:mygroup files* /somedir/
ADD --chown=bin files* /somedir/
ADD --chown=1 files* /somedir/
ADD --chown=10:11 files* /somedir/
```

示例：

```
ARG VERSION=7

FROM centos:7

# RUN useradd -u 9000 Yang; groupadd -g 9100 ChangZhou; groupadd -g 9200 JiangSu

ADD --chown=Yang:ChangZhou main.go /tmp/
ADD --chown=Yang:9100 home?.txt /tmp/
ADD --chown=9000:JiangSu home??.txt /tmp/

CMD ["/bin/echo/", "building finished"]
```

通过上述**Dockerfile**构建镜像时，会产生下面这样的错误。

```
unable to convert uid/gid chown string to host mapping: can't find gid for group ChangZhou: no such group: ChangZhou
```

虽然我们对**ADD指令**的定义没有任何问题，但是我们忽略了一个问题。由于我们在指令中使用的用户名和组名，以及**用户ID**和**组ID**在系统中并不存在，所以构建镜像时，无法&#x4ECE;**/etc/passwd**和/**etc/group**中找到相关记录(在**Linux**中创建**用户**和**组**时，用户的记录会被保存&#x5728;**/etc/passwd**中，而组的记录会被保存&#x5728;**/etc/group**文件中)，因此构建失败。

如何解决上述问题？

```
ARG VERSION=7

FROM centos:7

RUN useradd -u 9000 Yang; groupadd -g 9100 ChangZhou; groupadd -g 9200 JiangSu

ADD --chown=Yang:ChangZhou main.go /tmp/
ADD --chown=Yang:9100 home?.txt /tmp/
ADD --chown=9000:JiangSu home??.txt /tmp/

CMD ["/bin/echo/", "building finished"]
```

通过在**ADD指令**之前，创建好**ADD指令**所需的用户和组，就能够解决上述问题。这里我们通过**RUN指令**来执行这些创建任务。

如果容器的根文件系统不包&#x542B;**/etc/passwd**&#x548C;**/etc/group**文件，并且以**用户**或**组名**的形式使&#x7528;**--chown**，那么关于**ADD**构建的操作将会失败。使用**数值ID**形式的**用户**或**组**，则不需要查询且不会去依赖容器根文件系统的内容。

如果是一个**URL**形式的远端文件，目标文件将拥有**600**的文件权限。如果被检索的远端文件拥有一个**HTTP** **Last-Modified头部**，那么来自头部的时间戳将被用于设置目标文件的**mtime**。然而，像在**ADD**期间被处理的任意其他文件那样，**mtime**不会被用来决定文件是否被更改和缓存是否应该被更新。

**NOTE:**

在构建镜像时，如果通过标准输入来传递**Dockerfile（docker build - < somefile)**，则不会存在构建上下文，因此**Dockerfile**中只能使用基于**URL**的**ADD指令**。我们也可以通过标准输入传递一个压缩文件:**(docker build - < archive.tar.gz)**, 在压缩文件根目录下的**Dockerfile**和余下的一些文件将被作为构建上下文。

如果**URL**文件需要进行身份认证，那么我们需要使用**RUN wget**，**RUN curl**或者使用容器内部的其他工具来获取文件，因为**ADD指令**不支持身份认证功能。

**NOTE:**

如&#x679C;**\<src>**&#x4E2D;的内容被更改了，在构建镜像时，遇到的第一个**ADD指令**会使后续指令的缓存失效。这其中还包含了**RUN指令**的缓存。查看[**Dockerfile Best Practice guide - Leverage build cache**](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#leverage-build-cache)来获取更多信息。

**ADD指令遵照以下规则：**

* **\<src>路径必须包含在构建上下文之中；不能使用ADD ../something  /something, 因为docker build的第一步就是将上下文目录（及其子目录）发送给docker守护进程。**
* **如果\<src>是一个URL，并且\<dest>并没有以斜线结尾，那么将会从URL下载一个文件然后拷贝\<dest>。**
* **如果\<src>是一个URL，并且\<dest>以斜线结尾，那么docker会根据URL推断出文件名，然后文件会下载到\<dest>/\<filename>。例如，ADD** [**http://example.com/foobar**](http://example.com/foobar)  **/ 将创建一个/foobar文件。URL必须包含一个资源路径，以便可以通过这个路径找到适当的文件名。（**[**http://example.com**](http://example.com)**将无法执行)。**

**NOTE:**

**目录本身并不会被拷贝，仅仅只是拷贝目录中的内容。**

* 如&#x679C;**\<src>**&#x662F;一个能够被识别的本地压缩文件(例如**identity**，**gzip**，**bzip2**或**xz**)，那么它会被解压成一个目录。来自远端**URL**的资源无法被解压。当一个目录被拷贝或者解压时，它与**tar -x**命令有着相同的行为。

**NOTE:**

文件是否被识别为压缩格式的文件仅基于文件的内容，而不是文件的名称。例如，如果一个空文件碰巧&#x4EE5;**.tar.gz**结尾，这个文件不会被识别为压缩文件，也不会生成任何类型的减压错误消息，而是将这个文件简单地复制到目的地。

* 如&#x679C;**\<src>**&#x662F;任意其他类型的文件，它和它的元数据会被单独拷贝。在这种情况下，如&#x679C;**\<dest>**&#x4EE5;斜线结尾，它会被当作目录，**\<src>**&#x7684;内容会被写入&#x5230;**\<dest>/base(\<src>)**。
* 如果通过直接或使用通配符的方式指定了多&#x4E2A;**\<src>**&#x8D44;源，那&#x4E48;**\<dest>**&#x5FC5;须是一个目录，并且必须以斜线结尾。
* 如&#x679C;**\<dest>**&#x6CA1;有以斜线结尾，那么它会被当成一个正常的文件，&#x800C;**\<src>**&#x7684;内容会被写入到这个文件中。
* 如&#x679C;**\<dest>**&#x4E0D;存在，那么会在它的路径下创建所有缺失的目录。
