これは、なにをしたくて書いたもの?
時々、Dockerfile内で複数行の文字列を作りたくなる時があるのですが、どうやったらいいのかすぐに忘れるのでメモして
おこうかな、と。
環境
今回の環境は、こちらです。
$ docker version Client: Docker Engine - Community Version: 20.10.6 API version: 1.41 Go version: go1.13.15 Git commit: 370c289 Built: Fri Apr 9 22:47:17 2021 OS/Arch: linux/amd64 Context: default Experimental: true Server: Docker Engine - Community Engine: Version: 20.10.6 API version: 1.41 (minimum version 1.12) Go version: go1.13.15 Git commit: 8728dd2 Built: Fri Apr 9 22:45:28 2021 OS/Arch: linux/amd64 Experimental: false containerd: Version: 1.4.4 GitCommit: 05f951a3781f4f2c1911b05e61c160e9c30eaa8e runc: Version: 1.0.0-rc93 GitCommit: 12644e614e25b05da6fd08a38ffa0cfe1903fdec docker-init: Version: 0.19.0 GitCommit: de40ad0
結論
試した結果としては、複数行の文字列を書く…とはちょっと違いますが、シェルのグルーピング(Compound Commands)を
使うのがよいでしょう。
Command Grouping (Bash Reference Manual)
bash(1): GNU Bourne-Again SHell - Linux man page
こんな感じです。
RUN { \ echo '文字列1'; \ echo '文字列2'; \ echo '文字列3'; \ }
{}
は、()
にしてもOKです。
RUN ( \ echo '文字列1'; \ echo '文字列2'; \ echo '文字列3'; \ )
echoを繰り返しているだけでは?と思うかもですが、コマンドをグルーピングするとリダイレクトを1回で済ませることが
できます。
RUN { \ echo '文字列1'; \ echo '文字列2'; \ echo '文字列3'; \ } > /tmp/test1 RUN ( \ echo '文字列1'; \ echo '文字列2'; \ echo '文字列3'; \ ) > /tmp/test2
他の方法としては、echoコマンドの-n
オプションを使って以下のように書けばよいみたいです(dash)。Ubuntu Linuxなど。
RUN echo -n "文字列1\n\ 文字列2\n\ 文字列3\n" RUN echo -n '文字列1\n\ 文字列2\n\ 文字列3\n'
もしくは、こちら(bash、ash)。CentOSやAlpine Linuxなど。
RUN echo -n $'文字列1\n\ 文字列2\n\ 文字列3\n'
bashやashの場合は、$
が必要で、クォートは'
(シングルクォート)である必要があります。"
(ダブルクォート)ではうまく
いきません。
echoの-n
オプションは、\n
のような\
の部分をエスケープとして解釈してくれるようになります。
enable interpretation of backslash escapes
echo(1): line of text - Linux man page
If -e is in effect, the following sequences are recognized: \\ backslash \a alert (BEL) \b backspace \c produce no further output \e escape \f form feed \n new line \r carriage return \t horizontal tab \v vertical tab \0NNN byte with octal value NNN (1 to 3 digits) \xHH byte with hexadecimal value HH (1 to 2 digits)
-n
の有無で、こういう差が出ます。
$ echo '\n' \n
$ echo -e '\n'
つまり、\n\
で改行と継続行です。
ちなみに、クォートの閉じの前を\
にするとエラーになります。
RUN echo -n "文字列1\n\ 文字列2\n\ 文字列3\n\"
/bin/sh: 1: Syntax error: Unterminated quoted string
最後の\
を削除するか、以下のように改行を加えましょう。
RUN echo -n "文字列1\n\ 文字列2\n\ 文字列3\n\ "
お題
複数行の文字列、ということで。今回は以下の内容を複数行の文字列で記述して、/root/.bashrc
に追記するということを
やってみます。
alias helloworld="echo 'Hello World!!'" alias greeting="echo 'Welcome!!'" ### もしくは alias helloworld='echo "Hello World!!"' alias greeting='echo "Welcome!!"'
グルーピング(Compound Commands)を使った例
最初に、結果を。ここに書いたものをecho -n
で試行錯誤するのが、後ろに続きます。
RUN { \ echo ''; \ echo "alias helloworld='echo "'"'"Hello World!!"'"'"'"; \ echo "alias greeting='echo "'"'"Welcome!!"'"'"'" ; \ echo; \ } >> /root/.bashrc
変数展開を使った例。
ARG message1='Hello World!!' ARG message2='Welcome!!' RUN { \ echo ''; \ echo "alias helloworld='echo "'"'"${message1}"'"'"'"; \ echo "alias greeting='echo "'"'"${message2}"'"'"'" ; \ echo; \ } >> /root/.bashrc
結果は、どちらも以下になります。
# tail -n 6 /root/.bashrc # . /etc/bash_completion #fi alias helloworld='echo "Hello World!!"' alias greeting='echo "Welcome!!"'
Ubuntu Linux 20.04 LTS、CentOS 8、Alpine Linux 3.13(.bashrc
はないですが)のいずれでも同じことができます。
このあとは、echo -n
を使って頑張ることに興味がある人は読んでみてください。
Ubuntu Linuxで試す
Ubuntu Linux 20.04 LTSのイメージを使って、いろんなバリエーションでやってみましょう。
Dockerfile
FROM ubuntu:20.04 RUN echo -n '\n\ alias helloworld="echo '"'"'Hello World!!'"'"'"\n\ alias greeting="echo '"'"'Welcome!!'"'"'"\n\ \n\ ' >> /root/.bashrc
いろいろくっついていますが、'
で文字列を開始した中に'
を入れようと頑張っているだけです。
alias helloworld="echo '"'"'Hello World!!'"'"'"\n\ alias greeting="echo '"'"'Welcome!!'"'"'"\n\
Dockerイメージをビルド。
$ docker image build -t kazuhira/ubuntu:20.04 .
実行。
$ docker container run -it --rm kazuhira/ubuntu:20.04 root@710cc3521c1a:/#
確認。
# tail -n 6 /root/.bashrc # . /etc/bash_completion #fi alias helloworld="echo 'Hello World!!'" alias greeting="echo 'Welcome!!'"
OKですね。
# helloworld Hello World!! # greeting Welcome!!
クォートを"
にしてみます。
RUN echo -n "\n\ alias helloworld='echo "'"'"Hello World!!"'"'"'\n\ alias greeting='echo "'"'"Welcome!!"'"'"'\n\ \n\ " >> /root/.bashrc
確認。
# tail -n 6 /root/.bashrc # . /etc/bash_completion #fi alias helloworld='echo "Hello World!!"' alias greeting='echo "Welcome!!"'
alias
を使った実行結果は同じなので、省略。
'
と"
の差は想像に難くないですが、変数展開ですね。
FROM ubuntu:20.04 ARG message1='Hello World!!' ARG message2='Welcome!!' RUN echo -n '\n\ alias helloworld="echo ${message1}"\n\ alias greeting="echo ${message2}"\n\ \n\ ' >> /root/.bashrc RUN echo -n "\n\ alias helloworld='echo ${message1}'\n\ alias greeting='echo ${message2}'\n\ \n\ " >> /root/.bashrc
結果。
# tail -n 10 /root/.bashrc # . /etc/bash_completion #fi alias helloworld="echo ${message1}" alias greeting="echo ${message2}" alias helloworld='echo Hello World!!' alias greeting='echo Welcome!!'
CentOS、Alpine Linux
続いて、CentOS 8で試してみましょう。
Dockerfile
FROM centos:8.3.2011 RUN echo -n $'\n\ alias helloworld="echo Hello World!!"\n\ alias greeting="echo Welcome!!"\n\ \n\ ' >> /root/.bashrc
Ubuntu Linuxの時のように、'
の中に'
を含めるのはちょっとムリそうでした…。
Dockerイメージのビルド。
$ docker image build -t kazuhira/centos:8.3.2011 .
確認。
# tail -n 6 /root/.bashrc . /etc/bashrc fi alias helloworld="echo Hello World!!" alias greeting="echo Welcome!!"
aliasの実行結果は割愛します。
クォートが'
しか使えないので、変数を含めた場合は
FROM centos:8.3.2011 ARG message1='Hello World!!' ARG message2='Welcome!!' RUN echo -n $'\n\ alias helloworld="echo ${message1}"\n\ alias greeting="echo ${message2}"\n\ \n\ ' >> /root/.bashrc
展開されずにそのまま残ってしまいます。
# tail -n 6 /root/.bashrc . /etc/bashrc fi alias helloworld="echo ${message1}" alias greeting="echo ${message2}"
最後は、Alpine Linux 3.13。Alpine Linuxには.bashrc
がないので、適当にファイルに出力して確認。
Dockerfile
FROM alpine:3.13.5 ARG message1='Hello World!!' ARG message2='Welcome!!' RUN echo -n $'\n\ alias helloworld="echo Hello World!!"\n\ alias greeting="echo Welcome!!"\n\ \n\ ' >> /tmp/test RUN echo -n $'\n\ alias helloworld="echo ${message1}"\n\ alias greeting="echo ${message2}"\n\ \n\ ' >> /tmp/test
ビルド。
$ docker image build -t kazuhira/alpine:3.13.5 .
確認。
$ docker container run -it --rm kazuhira/alpine:3.13.5 cat /tmp/test alias helloworld="echo Hello World!!" alias greeting="echo Welcome!!" alias helloworld="echo ${message1}" alias greeting="echo ${message2}"
CentOS 8と同じですね。
とまあ、echo -n
を使って頑張って複数行の文字列にした割には、グルーピング(Compound Commands)に比べると
報われないので。
素直にいきましょう。