VicXue
VicXue

Reputation: 319

How does escape character work in a Dockerfile?

I was reading the Dockerfile Reference's escape section. In the documentation, there is an example demonstrating how the default escape character '\' may cause trouble in a Windows system.

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

Results in:

PS C:\John> docker build -t cmd .
Sending build context to Docker daemon 3.072 kB
Step 1/2 : FROM microsoft/nanoserver ---> 22738ff49c6d
Step 2/2 : COPY testfile.txt c:\RUN dir c:
GetFileAttributesEx c:RUN: The system cannot find the file specified.
PS C:\John>

According to my understanding, the first backslash in COPY testfile.txt c:\\ will escape the second backslash and the command becomes COPY testfile.txt c:\. The backslash disappears in the RUN dir c:\ command because it works as an escape character, which makes sense. However, I'm not sure why the newline character is also escaped, as the COPY and RUN commands were merged into a single command. I'm not an expert in escape characters and I might misunderstand something very basic here. Can you please enlighten me how escape characters work in this scenario? Thanks in advance.

Upvotes: 12

Views: 20692

Answers (2)

flen
flen

Reputation: 2396

Alright, this was quite painful, but I think I now have an answer.

TL;DR I think for all instructions other than RUN and CMD, using the "escape character" twice will escape itself. The Docker documentation seems out of date.


Full explanation

To run the example from the documentation that the OP posted, I believe one needs to switch the docker daemon to run Windows containers instead of Linux containers. I think this is at the very least necessary for pulling a Windows image without incurring in this error:

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

The Docker documentation needs to be updated, FROM windows/nanoserver will get you an error. Thus, I changed the FROM instruction. Here's my test 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  

This works! A few things to note:

  1. I couldn't reproduce the documentation bug, where:

The second \ at the end of the second line [i.e., the \ at the end of my first COPY instruction] would be interpreted as an escape for the newline, instead of a target of the escape from the first \ . I think the documentation is out of date.

I imagine they finally corrected this weird behavior.

  1. The documentation says:

escaping is not performed in a RUN command, except at the end of a line.

This should be valid for either a RUN or CMD instruction. The RUN instruction can be run in either of 2 forms (and the CMD in 3 forms), in shell or exec form. In shell form, it will read \ literally, except at the end of the line, where it will give you trouble by interpreting \ as an escape character, that will escape any white space till the next instruction. Thus:

   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

Interestingly, these 2 instructions will work though, I can't explain why the first one works:

   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://`

Anyhow, the best thing to do in these cases is to run these instructions in exec form instead. When in exec form, arguments are parsed as JSON, thus must be always enclosed in " and all \ need to be escaped.

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

Upvotes: 4

gregory
gregory

Reputation: 12993

Use the escape directive on Windows to avoid these headaches, e.g.:

# escape=`

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

In your case, the second slash is escaping the newline. Therefore two lines are running together to form: COPY testfile.txt c:\RUN dir c:. I understand you're thinking the first slash should escape the second, but that's not how the parser behaves according to the documentation.

Upvotes: 11

Related Questions