admin管理员组

文章数量:1312693

My ENV variable in the Dockerfile requires a newline sequence (\n). Using the ENV instruction, it seems impossible to achieve this.

I've tried all possible combinations, I think:

# Dockerfile

ENV FOO @static {\n path_regexp \\n}  # @static {n path_regexp \\n}
ENV BAR='@static {\n path_regexp \n}' # @static {\\n path_regexp \\n}
ENV BAZ="@static {\n path_regexp \n}" # @static {\\n path_regexp \\n}

\n is always prepended with a backslash. Is this a "feature"? How can I make my ENV respect the newline sequence?

Putting the variabile directly in Compose file will output the variabile correctly.

My ENV variable in the Dockerfile requires a newline sequence (\n). Using the ENV instruction, it seems impossible to achieve this.

I've tried all possible combinations, I think:

# Dockerfile

ENV FOO @static {\n path_regexp \\n}  # @static {n path_regexp \\n}
ENV BAR='@static {\n path_regexp \n}' # @static {\\n path_regexp \\n}
ENV BAZ="@static {\n path_regexp \n}" # @static {\\n path_regexp \\n}

\n is always prepended with a backslash. Is this a "feature"? How can I make my ENV respect the newline sequence?

Putting the variabile directly in Compose file will output the variabile correctly.

Share Improve this question asked Feb 1 at 16:46 gremogremo 48.5k80 gold badges271 silver badges447 bronze badges 4
  • The newline isn't being escaped. Why do you think it is? If you run env in the container you'll see the variables are all as you expect – Paolo Commented Feb 3 at 8:55
  • Do you need an actual newline, or a backslash n, in your environment variable? – BMitch Commented Feb 24 at 21:52
  • @BMitch an actual newline. The environment variable is used by Caddy web server which is very sensitive about that. – gremo Commented Feb 26 at 17:06
  • 2 I believe this is a duplicate of stackoverflow/q/50299617/596285 – BMitch Commented Feb 26 at 21:47
Add a comment  | 

5 Answers 5

Reset to default 3

Docker doesn't replace escaped characters in the ENV statement.

You can replace them at runtime by echoing them into new variables as this shows

FROM debian
ENV foo="Hello\nWorld!"
CMD bar=$(echo ${foo}) && echo ${#foo} && echo ${#bar}

It prints 13 and 12 showing that foo is 13 characters long and bar is 12 long, so \n has been replaced with a newline.

I don't believe you can do this with only a Dockerfile. The parsing on the ENV step is very minimal. However, you can define the variable outside of the build, and pass it in as a build arg:

$ cat df.env
from alpine

arg hello
env hello=${hello}
cmd env

$ hello="hello
> world"

$ docker build -t test-env -f df.env --build-arg "hello=$hello" .
[+] Building 0.2s (5/5) FINISHED                                                          docker:default
 => [internal] load build definition from df.env                                                   0.0s
 => => transferring dockerfile: 84B                                                                 0.0s
 => WARN: JSONArgsRecommended: JSON arguments recommended for cmd to prevent unintended behavior r  0.0s
 => [internal] load metadata for docker.io/library/alpine:latest                                    0.0s
 => [internal] load .dockerignore                                                                   0.0s
 => => transferring context: 49B                                                                    0.0s
 => CACHED [1/1] FROM docker.io/library/alpine:latest                                               0.0s
 => exporting to image                                                                              0.0s
 => => exporting layers                                                                             0.0s
 => => writing image sha256:11ff645049d1b266b68f2b0fe1da19ef0daccaf103bbafc2030b10d58eb0d6b6        0.0s
 => => naming to docker.io/library/test-env                                                         0.0s

 1 warning found (use docker --debug to expand):
 - JSONArgsRecommended: JSON arguments recommended for cmd to prevent unintended behavior related to OS signals (line 5)

$ docker run --rm test-env
HOSTNAME=af8f8744374d
SHLVL=1
HOME=/root
hello=hello
world
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PWD=/

Another option to do this is to expand the variable inside of an entrypoint script, in this case using printf. This will only apply to child processes of the entrypoint, so a docker exec would see the unexpanded variable:

from alpine

env hello=hello\\nworld

copy --chmod=755 <<-"EOT" /entrypoint.sh
#!/bin/sh
export hello="$(printf "$hello")"
if [ $# = 0 ]; then
  set -- /bin/sh
fi
exec "$@"
EOT

entrypoint [ "/entrypoint.sh" ]
cmd env

Multiline ENV variables in a Dockerfile

Docker will always automatically escape literal \n in a Dockerfile ENV instruction. Therefore you need some workaround

a) e.g. unescaping on startup:


# Define ENV variable (Docker escapes \n to \\n)
ENV FOO="@static {\n path_regexp \n}"

# unescape just-in-time using printf %b
CMD ["/bin/sh", "-c", "export FOO=$(printf '%b' \"$FOO\") && exec your_application"]

b) you could create a docker ENTRYPOINT script manually with the varible(s) defined there (probably the cleanest way)

ENTRYPOINT ["/entrypoint.sh"]
CMD ["your_application"]

c) in case you don't want to change or mess with a potentially inherited ENTRYPOINT - you could dynamically create a startup script:

# Create a startup script that contains the vars
RUN cat << 'EOF' > /startup.sh
#!/bin/sh
export FOO="@static {\n path_regexp \n}"
exec "$@"
EOF
RUN chmod +x /startup.sh

CMD ["/bin/sh", "-c", "source /startup.sh && exec your_application"]

Answer to your first statement:

"Using the ENV instruction, it seems impossible to achieve this."

Yes, because Docker’s ENV instruction sets variables as single-line strings and treats \n literally instead of as a newline (0x0A).

Since Docker Compose works (it supports multi-line YAML), you can alternatively use a shell command in the Dockerfile:

Dockerfile

➜  Documents cat Dockerfile 
FROM debian
ENV FOO="@static {\\n path_regexp \\n}"

RUN echo 'export FOO=$(echo -e "$FOO")' >> /root/.bashrc
CMD ["/bin/bash"]

This interprets \n as a newline.

➜  Documents docker run -it test-newline /bin/bash
root@1ef320838ff3:/# env
HOSTNAME=1ef320838ff3
PWD=/
FOO=@static {
 path_regexp 
}
HOME=/root
TERM=xterm
SHLVL=1
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
_=/usr/bin/env
root@1ef320838ff3:/# exit 

Hope that solves it!

New lines are not escaped, probably the tests that you are using is printing the output in some other format. Here are two examples that you can try:

FROM alpine

ENV FOO="Hello\nWorld!"

CMD ["sh", "-c", "echo -e ${FOO}"] 

and

FROM alpine

ENV FOO='echo -e "Hello\nWorld!"'

CMD ["sh", "-c", "$FOO"] 

本文标签: