bash 在 Docker 中使用字符串中的变量插值

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/40902445/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 22:28:10  来源:igfitidea点击:

Using variable interpolation in string in Docker

bashdocker

提问by Alexander Mills

I am having trouble creating and using variables in a Dockerfile - I build a Docker image via a Dockerfile with this command:

我在 Dockerfile 中创建和使用变量时遇到问题 - 我使用以下命令通过 Dockerfile 构建了一个 Docker 映像:

$ docker build --build-arg s=scripts/a.sh -t a .

(So because I use --build-arg, $s will be an available argument in the Dockerfile, and this part works)

(因为我使用 --build-arg,$s 将是 Dockerfile 中的一个可用参数,这部分有效)

The Dockerfile is like so:

Dockerfile 是这样的:

ARG s

RUN echo $s 

RUN useradd -ms /bin/bash newuser

USER newuser
WORKDIR /home/newuser

ENV fn=$(filename $s)  # fails on this line

COPY $s .

ENTRYPOINT ["/bin/bash", "/home/newuser/$fn"]

The problem I have is that the Docker build is failing on the line indicated above.

我遇到的问题是 Docker 构建在上面指出的行上失败。

Error response from daemon: Syntax error - can't find = in "$s)". Must be of the form: name=value

If I change that line to this:

如果我将该行更改为:

RUN fn=$(filename $s)

I get this error:

我收到此错误:

Error: Command failed: docker build --build-arg s=scripts/a.sh -t a .
The command '/bin/sh -c fn=$(filename $s)' returned a non-zero code: 127

Anyone know the correct way to

任何人都知道正确的方法

  1. Create a variable inside the docker file
  2. Use string interpolation with that variable so that I can reference the variable in the ENTRYPOINT arguments like so:

    ENTRYPOINT ["/bin/bash", "/home/newuser/$var"]

  1. 在 docker 文件中创建一个变量
  2. 对该变量使用字符串插值,以便我可以在 ENTRYPOINT 参数中引用该变量,如下所示:

    入口点 ["/bin/bash", "/home/newuser/$var"]

Even if I do this:

即使我这样做:

ARG s

ARG sname

RUN echo $s          # works as expected
RUN echo $sname      # works as expected

RUN useradd -ms /bin/bash newuser

USER newuser
WORKDIR /home/newuser


COPY $s .  # works as expected (I believe)

ENTRYPOINT /bin/bash /home/newuser/$sname  # does not work as expected

even though I am using the "non-JSON" version of ENTRYPOINT, it still doesn't seem to pick up the value for the $snamevariable.

即使我使用的是“非 JSON”版本的 ENTRYPOINT,它似乎仍然没有获取$sname变量的值。

回答by Villlem

I would avoid using variable in ENTRYPOINTat all. It's tricky and requires a deep understanding of what is going on. And is easy to break it by accident. Just consider one of the following.

我会完全避免使用变量ENTRYPOINT。这很棘手,需要深入了解正在发生的事情。而且很容易不小心弄坏。只需考虑以下其中一项。

Create link with the known name to your start script.

创建具有已知名称的链接到您的启动脚本。

RUN ln -s /home/newuser/$sname /home/newuser/docker_entrypoint.sh
ENTRYPOINT ["/home/newuser/docker_entrypoint.sh"]

or write standalone entrypoint script that runs what you need.

或编写运行您需要的独立入口点脚本。

But if you want to know how and why solutions in your questions work just keep reading.

但是,如果您想知道问题中的解决方案如何以及为什么起作用,请继续阅读。

First some definitions.

首先是一些定义。

  • ENV- is environmentvariable available during buildtime (docker build) and runtime (docker run)
  • ARG- is environmentvariable available only during buildtime
  • ENV-在构建时 ( ) 和运行时 ( )期间环境变量是否可用docker builddocker run
  • ARG-环境变量是否仅在构建时可用

If you look at https://docs.docker.com/engine/reference/builder/#environment-replacementyou see the list of dockerfile instructions that support those environment variables directly. This is why COPY"picks up the variable" as you said.

如果您查看https://docs.docker.com/engine/reference/builder/#environment-replacement,您会看到直接支持这些环境变量的 dockerfile 指令列表。这就是为什么COPY如您所说的“选择变量”。

Please note that there is no RUNnor ENTRYPOINT. How does it work?

请注意,没有RUN也没有ENTRYPOINT。它是如何工作的?

You need to dig into the documentation. First RUN(https://docs.docker.com/engine/reference/builder/#run). There are 2 forms. The first one executes command through the shell and this shell has access to buildtime environment variables.

您需要深入研究文档。首先RUNhttps://docs.docker.com/engine/reference/builder/#run)。有2种形式。第一个通过 shell 执行命令,这个 shell 可以访问构建时环境变量。

# this works because it is called as /bin/sh -c 'echo $sname'
# the /bin/sh replace $sname for you      
RUN echo $sname 

# this does NOT work. There is no shell process to do $sname replacement 
# for you
RUN ["echo", "$sname"]

Same thing applies to the ENTRYPOINTand CMDexcept only runtime variables are available during container start.

同样的事情适用于ENTRYPOINT并且CMD除了在容器启动期间只有运行时变量可用。

# first you need to make some runtime variable from builtime one
ENV sname $sname

# Then you can use it during container runtime
# docker runs `/bin/sh -c '/bin/bash /home/newuser/$sname'` for you
# and this `/bin/sh` proces interprets `$sname`
ENTRYPOINT /bin/bash /home/newuser/$sname

# but this does NOT work. There is no process to interpolate `$sname`
# docker runs what you describe.
ENTRYPOINT ["/bin/bash", "/home/newuser/$sname"]

edit 2017-04-03: updated links to the docker documentations and slight rewording to avoid confusion that I sense from other answers and comments.

编辑 2017-04-03:更新了 docker 文档的链接并稍微改写以避免我从其他答案和评论中感觉到的混淆。

回答by Alexander Mills

I requested @Villem to answer, and his answer is much more definitive, but the following will work (just is not a stable solution). His answer is basically saying that this answer is not a good way to do it:

我要求@Villem 回答,他的回答更加明确,但以下方法可行(只是不是一个稳定的解决方案)。他的回答基本上是说这个回答不是一个好方法:

ARG s           # passed in via --build-arg s=foo
ARG sname       # passed in via --build-arg sname=bar

RUN echo $s       
RUN echo $sname  

ENV sname $sname   # this is the key part

RUN useradd -ms /bin/bash newuser

USER newuser
WORKDIR /home/newuser

COPY $s .

ENTRYPOINT /bin/bash /home/newuser/$sname   # works, but is not stable!

don't ask me why the COPY command picks up the variable that was declared via ARG, but that the ENTRYPOINT command does notseem to pick up the variable declared via ARG, but only picks up the variable declared via ENV. At least, this appears to be the case.

不要问我为什么 COPY 命令会选取通过 ARG 声明的变量,但是 ENTRYPOINT 命令似乎没有选取通过 ARG 声明的变量,而只会选取通过 ENV 声明的变量。至少,情况似乎是这样。

回答by Sebien

I wanted both variable substitution and arguments passing.

我想要变量替换和参数传递。

Let's say our Dockerfile has:

假设我们的 Dockerfile 有:

ENV VARIABLE=replaced

And we want to run this:

我们想运行这个:

docker run <image> arg1 arg2

I obviously wanted this output:

我显然想要这个输出:

replaced arg1 arg2

I eventually found this one:

我最终找到了这个:

ENTRYPOINT [ "sh", "-c", "echo $VARIABLE 
docker run <image> --app.options.additional.second=true --app.options.additional.third=false

ENTRYPOINT [ "sh", "-c", "java -Xmx$XMX 
ARG install="bundle install --jobs=4"
FROM ruby:2.6.3-alpine

RUN eval $install
$@", \ "-jar", \ "/my.jar", \ "--app.options.first=true" ]
$@" ]

It works!!! But I feel SOOOO dirty!

有用!!!但我觉得太脏了!

Obviously, in real life, I wanted to do something more useful:

显然,在现实生活中,我想做一些更有用的事情:

FROM ruby:2.6.3-alpine

ARG install="bundle install --jobs=4"
RUN eval $install

Why a so complicated answer?

为什么这么复杂的答案?

  • "ENTRYPOINT java..." would not pass docker arguments to the entrypoint => "ENTRYPOINT [..." is mandatory for that
  • "ENTRYPOINT [..." will NOT call the shell, so no variable substitution is done at all => "sh -c" is mandatory for that
  • "sh -c" only take the FIRST argument passed to it and split it in command+arguments => so everything must be in the first argument of "sh -c" for variables to be visible by the command
  • Docker arguments are passed as extra array entries of "ENTRYPOINT [..." => so the "$@" is necessary to "copy" the remainings arguments of the "sh -c" into the "echo ..." command to be executed (and as a bonus, we can reuse the additional array entries of ENTRYPOINT[] to place forced arguments in a readable way in the Dockerfile)
  • "$@" removes the first argument => so explicit "$0" must be used before "$@"
  • “ENTRYPOINT java...”不会将docker参数传递给入口点=>“ENTRYPOINT [...”是强制性的
  • "ENTRYPOINT [..." 不会调用 shell,所以根本不进行变量替换 => "sh -c" 是强制性的
  • "sh -c" 只接受传递给它的第一个参数并将其拆分为 command+arguments => 所以一切都必须在 "sh -c" 的第一个参数中,以便命令可以看到变量
  • Docker 参数作为“ENTRYPOINT [...” => 的额外数组条目传递,因此必须使用“$@”将“sh -c”的剩余参数“复制”到“echo ...”命令中被执行(作为奖励,我们可以重用 ENTRYPOINT[] 的附加数组条目以在 Dockerfile 中以可读的方式放置强制参数)
  • "$@" 删除第一个参数 => 因此必须在 "$@" 之前使用显式的 "$0"

Fiou...

飞...

I added a comment to this issue, for Docker developers to see what we are forced to do and perhaps change their mind to make ENTRYPOINT[] replace environment variables: https://github.com/moby/moby/issues/4783#issuecomment-442466609

我在这个问题上添加了一条评论,让 Docker 开发人员看到我们被迫做什么,也许会改变主意让 ENTRYPOINT[] 替换环境变量:https: //github.com/moby/moby/issues/4783#issuecomment -442466609

回答by Constantin De La Roche

I spent a lot of time to find out that.

我花了很多时间才知道。

Don't works !

不工作!

docker build -t server --no-cache --build-arg install="bundle install --without development test" .`

But this works...

但这有效...

##代码##

Then to build the docker image:

然后构建docker镜像:

##代码##