Skip to content

dockerfile: keep escapes in RUN step names - #7184

Open
Suhail98 wants to merge 3 commits into
moby:masterfrom
Suhail98:dockerfile-run-name-escapes
Open

Suhail98 wants to merge 3 commits into
moby:masterfrom
Suhail98:dockerfile-run-name-escapes

Conversation

@Suhail98

@Suhail98 Suhail98 commented Sep 23, 2026

Copy link
Copy Markdown

fixes #5250

Build progress drops backslashes from RUN step names, which makes Windows paths unreadable. RUN echo C:\hello\world is shown as RUN echo C:helloworld, even though the shell receives the command unchanged.

  • The name comes from passing the line through the word lexer to expand known variables. That lexer also strips escapes.
  • dispatchRun now sets RawEscapes next to the existing RawQuotes. Escapes still apply while lexing, so \$FOO stays unexpanded, but they remain in the name.
  • This exposed a RawEscapes bug in the lexer. When the quotes are kept, an escape character inside double quotes that escapes nothing was written twice ("C:\hello" became "C:\\hello"). Without RawQuotes the doubling is still needed, since ${FOO#"a\b"} relies on it, so that case is unchanged.
  • The fix is not Windows-specific and works the same with # escape=`. Unlike fix: dockerfile2llb: handle escaping of backslashes correctly #5269, processCmdEnv is unchanged, so COPY, ADD and WORKDIR names stay the same.

Visible change: exec-form lines keep their JSON escapes, so RUN ["echo", "C:\\path"] is shown as written.

For context, testSecretAsEnviron already uses forward slashes on Windows "because the Dockerfile parser consumes backslashes as escapes".

Testing: the new tests in dockerfile2llb (step names read from Dockerfile2LLB output) and shell fail on master and pass with the fix. The existing shell, parser, instructions and dockerfile2llb unit tests pass.

The progress name of a RUN step is the Dockerfile line passed through
the word lexer so that known variables are expanded. The command itself
is passed to the shell unchanged, so escapes removed by the lexer only
disappear from the name: "RUN echo C:\hello\world" was shown as
"RUN echo C:helloworld".

Keep escape characters in the name, as is already done for quotes. The
lexer still honours them, so "\$FOO" stays unexpanded.

This exposed a bug in the lexer's raw-escape mode: inside double quotes,
an escape character that escapes nothing was written twice.

Signed-off-by: Suhail Hany <suhail9816@gmail.com>
t.Parallel()
df := `FROM scratch
ENV FOO=bar
RUN echo C:\hello\world\path

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't this require an # escape= annotation? IIRC, otherwise \ is still an escape?

https://docs.docker.com/reference/dockerfile#escape

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For RUN it isn't: the reference you linked says "regardless of whether the escape parser directive is included in a Dockerfile, escaping is not performed in a RUN command, except at the end of a line."

The shell-form command is passed to the shell as a literal string (handleJSONArgs), so sh -c or cmd /S /C gets echo C:\hello\world\path unchanged. Only the step name goes through the word lexer. That's why the issue shows RUN echo C:helloworldpath as the name, with C:\hello\world\path in the output right below it.

With # escape=` the same happens to backticks: RUN echo C:\hello `$FOO is shown as RUN echo C:\hello $FOO today. I added that case to the test.

Also, I pushed a second commit: my lexer change also affected ${VAR#"..."} patterns, so it's now limited to the case where the quotes are kept.

@thaJeztah thaJeztah Sep 23, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For RUN it isn't: the reference you linked says "regardless of whether the escape parser directive is included in a Dockerfile, escaping is not performed in a RUN command, except at the end of a line."

Ah, you're right! Yes, escaping in RUN is handled by whatever is executed by the RUN itself.

(except for the line-continuation at the end, which is part of the Dockerfile parsing)

The previous commit stopped raw-escape mode from writing a literal
escape character inside double quotes twice. That doubling is still
needed when the quotes themselves are dropped, as they are for the
patterns of ${VAR#...}, ${VAR%...} and ${VAR/.../...}: without it,
${FOO#"a\b"} fails with "invalid escape '\b'" instead of matching a
literal "a\b".

Only write the escape character once when the quotes are kept
(RawQuotes), as for RUN step names and heredoc detection.

Signed-off-by: Suhail Hany <suhail9816@gmail.com>
With "# escape=`" the escape character in RUN step names is a backtick,
which was dropped from the name the same way as a backslash.

Signed-off-by: Suhail Hany <suhail9816@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

progress: if not escaped, backslashes \ not printed on the progress stream dockerfile steps

2 participants