# 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;存在，那么会在它的路径下创建所有缺失的目录。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://camelgemonion.gitbook.io/docker/dockerfile-zhi-ling/add.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
