Jak pisać polecenia z wieloma wierszami w Dockerfile, zachowując nowe wiersze?

86

Chcę napisać następujące polecenie RUN w pliku Dockerfile. Jednak docker nie zachowuje nowych wierszy.

RUN echo "[repo] \
name            = YUM Repository \
baseurl         = https://example.com/packages/ \
enabled         = 1 \
gpgcheck        = 0" > /etc/yum.repos.d/Repo.repoxyz

Wiem, że \na końcu każdej linii ucieka nowa linia. Ale czy jest jakiś sposób, żebym mógł napisać wiele wierszy z zachowaniem nowego wiersza?

Venkata Jaswanth
źródło
Czy używasz platformy innej niż * nix? Ponieważ działa to dobrze dla mnie w systemie Linux.
użytkownik
@user, używałem Linuksa.
Venkata Jaswanth,

Odpowiedzi:

115

Możesz używać tak zwanego „cytowania ANSI-C” z $'...'. Pierwotnie była to funkcja ksh93, ale teraz jest dostępna w bash, zsh, mksh, FreeBSD sh oraz w popiele busyboxa (ale tylko wtedy, gdy jest skompilowany z ENABLE_ASH_BASH_COMPAT).

Ponieważ RUN używa /bin/shdomyślnie powłoki jako powłoki, musisz najpierw przełączyć się na coś takiego jak bash, używając instrukcji SHELL.

Rozpocznij polecenie $', zakończ je 'i używaj \n\do nowych linii, na przykład:

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
Daniel Zolnai
źródło
4
To jest składnia basha. Zobacz to pytanie, aby uzyskać więcej informacji: stackoverflow.com/a/11966402/1395437
Daniel Zolnai
13
Proszę, wyjaśnij, co robisz, nie upuszczaj nieprzezroczystego rozwiązania
Édouard Lopez
25
Słowo ostrzeżenia: $' ... \n\ technika zależy od powłoki, której docker RUNużywa bash. W niektórych systemach (takich jak Ubuntu) powłoka RUN używa /bin/shczęsto łącza, do dashktórego NIE jest bashi nie rozumie $'składni.
Anon
2
jak powiedział @Anon, to NIE DZIAŁA, jeśli zostanie uruchomione w innym miejscu niż bash. (w tym też nie działa Oh My Zsh)
Rafa
1
@Anon dobrze działa w Linuksie Alpine (obecnie jestem na 3.10.2), który używa powłoki.
suchoss
53

Użyłem printf. Pisanie całego tekstu w jednej linii za pomocą "\ n".

Wykonywanie:

RUN printf 'example \ntext \nhere' >> example.txt

wkładki:

example
text
here

w example.txt

CTodea
źródło
19

Możesz użyć:

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

W ten sposób będziesz mieć szybki sposób sprawdzenia, jaka jest zawartość pliku. Musisz tylko mieć świadomość, że musisz kończyć każdą linię \i wstawiać, \ngdy jest to potrzebne.

Paulo Fidalgo
źródło
1
To wymaga echo -einterpretacji\n
Patrick Bergner
7

Skończyło się na tym, że użyłem kombinacji przykładów wymienionych powyżej, ponieważ nowa linia \nnie działała echo.

RUN printf 'example \n\
text \n\
here' >> example.txt

Zgodnie z oczekiwaniami generuje:

example
text
here
Siergiej
źródło
1
Czym twoja odpowiedź różni się od tej CTodea?
Ojciec chrzestny
@TheGodfather To przykład wielowierszowy. Lepiej też odpowiada na oryginalne pytanie.
Sergey
2

Może to ci pomoże ( 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
jen-soft
źródło
-1

Możesz wykonać RUN kilka razy, aby ukończyć plik:

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

Może to nie być optymalne rozwiązanie, ponieważ tworzy nową warstwę dla każdego polecenia RUN. Mimo to każda warstwa będzie tak duża, jak wprowadzona przez Ciebie zmiana, czyli w tym przypadku w kolejności bajtów (pierwsza warstwa RUN powinna mieć rozmiar 7-bajtowy).

Zaletą tego rozwiązania jest to, że będzie działać ze wszystkimi powłokami.

Abel
źródło
2
Prawdopodobnie lepiej połączyć te polecenia z &&lepszymi celami buforowania, krótszym rejestrowaniem i krótszym Dockerfileczasem kompilacji
JohannesB