docker sh vs bash 变量 ${@} 扩展 - ${@} 在 sh 中不起作用

docker sh vs bash variable ${@} expansion - ${@} doesn't work in sh

这真的很简单:我正在尝试构建一个 docker 图像,其中入口点接收传递的 args。当我使用 bash shell 时,它起作用了。当我使用 alpine 附带的 sh 时,它不会扩展;我认为这是因为 sh 根本没有参数

这可能吗?而且,如何?

这行不通;意思是 docker run -it myimage arg1 arg2 没有收到 arg1 arg2 作为 ${@} 的要求。我希望 gradlew 会被调用为 ./gradlew arg1 arg2

FROM openjdk:8u212-jre-alpine
RUN apk add --no-cache jq openjdk8 python3 \
    && pip3 install --upgrade pip && pip3 install yq
COPY . /root
WORKDIR /root
RUN ./gradlew dependencies &> /dev/null
ENTRYPOINT [ "sh", "-c", "./gradlew ${@}" ]

这就是:

FROM gradle
COPY . /home/gradle
RUN ./gradlew -v && ./gradlew dependencies &> /dev/null
ENTRYPOINT [ "bash", "-c", "./gradlew ${@}" ]

当我深入了解 sh 的真正含义时:

/bin # which sh
/bin/sh
/bin # ls -al /bin/sh
lrwxrwxrwx    1 root     root            12 May  9  2019 /bin/sh -> /bin/busybox

我对“gradle”一无所知,所以让我们用 echo 语句替换对 gradlew 的调用,以便进行诊断:

FROM openjdk:8u212-jre-alpine
ENTRYPOINT [ "sh", "-c", "echo ${@}" ]

如果我构建一个名为 cbtest 和 运行 的图像,它会像这样:

docker run --rm cbtest arg1 arg2

我得到输出:

arg2

这意味着它大部分时间都在工作,除了我在评论中指出你失去了你的第一个论点。如果我们看一下 bash(1) 手册页,我们会发现:

-c  If  the  -c option is present, then commands are read from the
    first non-option argument  command_string.   If  there  are
    arguments  after  the  command_string,  the  first  argument is
    assigned to [=13=] and any remaining argu‐ ments are assigned to the
    positional parameters.  The assignment to [=13=]  sets the name of the
    shell, which is used in warning and error messages.

关键短语是第一个参数分配给$0。这 参数 [=28=] 是当前 运行ning 程序的名称,而不是 而不是程序的参数。您需要在 sh 中添加一个 -- 命令行指示它应该停止尝试解析参数 并将所有内容作为参数传递给被调用的脚本。再次来自手册页:

--  A -- signals the end of options and disables further option
    processing.  Any arguments  after the -- are treated as filenames
    and arguments.  An argument of - is equivalent to --.

这给了我们:

FROM openjdk:8u212-jre-alpine
ENTRYPOINT [ "sh", "-c", "echo ${@}", "--" ]

如果我 运行 与上面相同的命令,我现在得到:

arg1 arg2

太好了。

我在评论中提到您滥用了 ${@} 变量 因为你没有引用它。你可能认为你是,但是 /bin/sh 您拨打的电话对这些报价一无所知;这 传递给 shell 的命令很简单:

./gradlew ${@}

...${@} 周围没有引号。让我们稍微修改一下脚本 我们可以看到差异;考虑这个脚本:

FROM openjdk:8u212-jre-alpine
ENTRYPOINT [ "sh", "-c", "for arg in ${@}; do echo $arg; done", "--" ]

如果我运行这样的图片:

docker run --rm cbtest "arg with spaces" "second arg"

我得到输出:

arg
with
spaces
second
arg

如您所见,脚本有五个参数而不是两个。 我们可以通过适当的引用来解决这个问题:

FROM openjdk:8u212-jre-alpine
ENTRYPOINT [ "sh", "-c", "for arg in \"${@}\"; do echo $arg; done", "--" ]

根据上述输入产生:

arg with spaces
second arg

所以你的 ENTRYPOINT 应该是这样的:

ENTRYPOINT [ "sh", "-c", "./gradlew \"${@}\"", "--" ]