How to write commands with multiple lines in Dockerfile while preserving the new lines?
DockerDockerfileDocker Problem Overview
I want to write the following RUN command in the Dockerfile. But, docker is not preserving the new lines.
RUN echo "[repo] \
name = YUM Repository \
baseurl = https://example.com/packages/ \
enabled = 1 \
gpgcheck = 0" > /etc/yum.repos.d/Repo.repoxyz
I know that \
at the end of each line escapes the new line. But, is there any way that I can write multiple lines preserving the new line?
Docker Solutions
Solution 1 - Docker
You can use what is called "ANSI-C quoting" with $'...'
. It was originally a ksh93 feature but it is now available in bash, zsh, mksh, FreeBSD sh and in busybox's ash (but only when it is compiled with ENABLE_ASH_BASH_COMPAT).
As RUN uses /bin/sh
as shell by default you are required to switch to something like bash first by using the SHELL instruction.
Start your command with $'
, end it with '
and use \n\
for newlines, like this:
SHELL ["/bin/bash", "-c"]
RUN echo $'[repo] \n\
name = YUM Repository \n\
baseurl = https://example.com/packages/ \n\
enabled = 1 \n\
gpgcheck = 0' > /etc/yum.repos.d/Repo.repoxyz
Solution 2 - Docker
I used printf
. Writing all the text in one line using \n
.
Executing:
RUN printf 'example \ntext \nhere' >> example.txt
inserts:
example
text
here
in example.txt
Solution 3 - Docker
You can use:
RUN echo -e "\
[repo] \n\
name = YUM Repository \n\
baseurl = https://example.com/packages/ \n\
enabled = 1 \n\
gpgcheck = 0\
" > /etc/yum.repos.d/Repo.repoxyz
This way you will have a quick way to check what the file contents are. You just need to be aware that you need to end every line with \
and insert the \n
when needed.
Solution 4 - Docker
I ended up using a combination of the examples listed above since the new line \n
did not work with echo
.
RUN printf 'example \n\
text \n\
here' >> example.txt
It produces the following, as expected:
example
text
here
Solution 5 - Docker
May be it's help you ( https://github.com/jen-soft/pydocker )
[ Dockerfile.py ]
from pydocker import DockerFile # sudo pip install -U pydocker
d = DockerFile(base_img='debian:8.2', name='jen-soft/custom-debian:8.2')
d.RUN_bash_script('/opt/set_repo.sh', r'''
cat >/etc/apt/sources.list <<EOL
deb http://security.debian.org/ jessie/updates main
deb-src http://security.debian.org/ jessie/updates main
EOL
apt-get clean && apt-get update
''')
d.EXPOSE = 80
d.WORKDIR = '/opt'
d.CMD = ["python", "--version"]
# d.generate_files()
d.build_img()
# sudo wget -qO- https://get.docker.com/ | sh
python Dockerfile.py
docker images
Solution 6 - Docker
As of Docker 18.09 and Dockerfile syntax 1.4, Dockerfiles support heredocs (ie what you're looking for here) natively!
- Enable BuildKit, eg by setting
DOCKER_BUILDKIT=1
. - Add this line to the top of your Dockerfile:
# syntax=docker/dockerfile:1.3-labs
- Rewrite your heredoc like so:
COPY <<EOF /etc/yum.repos.d/Repo.repoxyz [repo] name = YUM Repository baseurl = https://example.com/packages/ enabled = 1 gpgcheck = 0 EOF
You can also use this to run multiple bash commands in a single RUN
block, etc. More details: Docker blog post, Dockerfile syntax docs.
Solution 7 - Docker
You can execute RUN several times to complete your file:
RUN echo "[repo]" >> /etc/yum.repos.d/Repo.repoxyz
RUN echo "name = YUM Repository" >> /etc/yum.repos.d/Repo.repoxyz
RUN echo "baseurl = https://example.com/packages/" >> /etc/yum.repos.d/Repo.repoxyz
RUN echo "enabled = 1" >> /etc/yum.repos.d/Repo.repoxyz
RUN echo "gpgcheck = 0" >> /etc/yum.repos.d/Repo.repoxyz
This may not be the optimal solution because it creates a new layer for every RUN command. Still, every layer will be as big as the change you make, which in this case it's in the order of Bytes (first RUN layer should be 7-byte).
The benefit of this solution is that it will work with all shells.