转义字符如何在 Dockerfile 中工作?



我正在阅读Dockerfile Reference的转义部分。在文档中,有一个示例演示了默认转义字符"\"如何在 Windows 系统中造成问题。

FROM microsoft/nanoserver  
COPY testfile.txt c:\  
RUN dir c:  

结果:

PS C:\John> docker build -t cmd .
将构建上下文发送到 Docker 守护程序 3.072 kB 步骤 1/2 :
从 microsoft/nanoserver ---> 22738ff49c6d
步骤 2/2 : 复制测试文件.txt c:\RUN dir c:
GetFileAttributesEx c:RUN:系统找不到指定的文件。
PS C:\约翰>

根据我的理解,COPY testfile.txt c:\中的第一个反斜杠将转义第二个反斜杠,命令变为COPY testfile.txt c:。反斜杠在RUN dir c:命令中消失,因为它用作转义字符,这是有意义的。但是,我不确定为什么换行符也被转义,因为 COPY 和 RUN 命令合并为一个命令。我不是逃脱字符的专家,我可能会误解这里一些非常基本的东西。你能告诉我在这种情况下如何转义字符吗?提前谢谢。

在 Windows 上使用 escape 指令来避免这些麻烦,例如:

# escape=`
FROM microsoft/nanoserver
COPY testfile.txt c:
RUN dir c:

在您的情况下,第二个斜杠正在转义换行符。因此,两条线一起运行以形成:COPY testfile.txt c:RUN dir c:。我知道您认为第一个斜杠应该转义第二个斜杠,但根据文档,这不是解析器的行为方式。

好吧,这很痛苦,但我想我现在有一个答案。

TL;DR 我认为对于 RUN 和 CMD 以外的所有指令,使用两次"转义字符"会自行转义。Docker文档似乎已经过时了。

<小时 />

完整说明

要从 OP 发布的文档中运行示例,我认为需要切换 docker 守护进程以运行 Windows 容器而不是 Linux 容器。我认为这对于拉取 Windows 映像而不会引起此错误至少是必要的:

ltsc2022: Pulling from windows/nanoserver
no matching manifest for linux/amd64 in the manifest list entries

Docker 文档需要更新,FROM windows/nanoserver会给你一个错误。因此,我更改了FROM指令。这是我的测试Dockerfile:

# escape=
FROM mcr.microsoft.com/windows/nanoserver:win10-21h1-preview
COPY testfile.txt c:\
RUN dir c:
COPY \subdir\subfile.txt c:\newdir\
RUN dir c: /w
RUN dir /w c:subdir\   
RUN type c:testfile.txt  

这行得通!需要注意的几点:

  1. 我无法重现文档错误,其中:

第二行末尾的第二个 \ [即我的第一个 COPY 指令末尾的] 将被解释为换行符的转义,而不是从第一行 \ 转义的目标。我认为文档已过时。

我想他们终于纠正了这种奇怪的行为。

  1. 文档说:

转义不在 RUN 命令中执行,行尾除外。

这应该对 RUN 或 CMD 指令有效。RUN 指令可以以 2 种形式(CMD 以 3 种形式(、shell 或 exec 形式运行。在 shell 形式中,它会从字面上读,除了在行尾,它会通过将解释为转义字符来给你带来麻烦,这将在下一条指令之前转义任何空格。因此:

RUN dir c:
# breaks the code, because it will escape white space
# and add the next lines to this instruction. That is:
# RUN dir c:RUN dir...
RUN dir c:\
# also doesn't work, because then it runs as `cmd /S /C dir c:\
# which is invalid syntax. It must be `c:` instead

有趣的是,这 2 条指令会起作用,但我无法解释为什么第一个有效:

RUN dir c:newdir\
# works! But why does this work and `RUN dir c:\` doesn't?
RUN dir c: /w
# works because it doesn't end in `c://`

无论如何,在这些情况下,最好的办法是以执行形式运行这些指令。当采用 exec 形式时,参数被解析为 JSON,因此必须始终包含在"中,并且所有都需要转义。

RUN ["cmd", "/C", "dir", "c:\"]
# works

最新更新