Docker 文档的该部分中的表格在技术上并不正确:当存在 shell 形式的入口点时,Docker 实际上并没有删除或忽略命令部分。这里实际发生的(您的示例演示)是:
- 如果
ENTRYPOINT 或CMD(或两者都是)是shell 形式,则它被包裹在["/bin/sh", "-c", "..."] 中。
-
ENTRYPOINT 和 CMD 列表连接起来形成一个命令列表。
让我们举第三个例子。这是,在 Dockerfile 语法中,
ENTRYPOINT /bin/echo "(shell) ENTRYPOINT@image ($0)"
CMD ["/bin/cat"]
生成的组合命令是(在 JSON 数组语法中,为清楚起见进行了扩展)
[
"/bin/sh",
"-c",
"/bin/echo \"(shell) ENTRYPOINT@image ($0)\"",
"/bin/cat"
]
那么,如果你给sh -c 提供多个参数,它会做什么呢? POSIX spec for the sh command 将语法记录为
sh -c command_string [command_name [argument...]]
和其他文件
-c:从 command_string 操作数中读取命令。从 command_name 操作数的值中设置特殊参数 0 [...] 的值,从剩余的 参数 中依次设置位置参数($1、$2 等) em> 操作数。不得从标准输入中读取任何命令。
这就是您在示例中看到的。如果ENTRYPOINT是一个裸字符串,CMD是一个JSON数组,那么在ENTRYPOINT字符串命令中,CMD中的参数可以用作$0、$1等等。如果两者都是裸字符串,则都被包裹在 sh -c 中,你会得到类似的东西:
ENTRYPOINT /bin/echo "$0 is /bin/sh, $1 is -c, and then $2"
CMD the rest of the line
在您的第一个示例中,命令部分是空的,在这种情况下(仍然来自 POSIX sh 文档)
如果 command_name 没有指定,特殊参数 0 应设置为 [...] 通常是用于执行 sh 实用程序的路径名。
你的最后一个例子更微妙:
docker run -it sc-test:v4 "/bin/dog ($0)"
由于字符串是双引号,你的 local shell 扩展了其中的 $0 引用,这就是 bash 进入那里的方式;那么由于它是一个(引用的)单词,它成为sh -c 的单个 command_name 参数。
ENTRYPOINT 和 CMD 一起使用还有另外两种正常模式。我更喜欢的模式是CMD 是一个完整的shell 命令,ENTRYPOINT 进行一些首次设置,然后运行类似exec "$@" 的命令来运行该命令。还有一个“容器即命令”模式,其中ENTRYPOINT 有一个完整的命令(可能涉及JVM 参数)和CMD 附加选项。在这些情况下,ENTRYPOINT 必须是 JSON 数组语法:
ENTRYPOINT ["/script-that-exec-dollar-at.sh"]
CMD ["the_command", "--option"]
由于ENTRYPOINT 字符串不直接引用$0、$1、等。CMD 参数有效 em> 被 sh -c 包装器忽略。如果您有ENTRYPOINT script.sh,它将被sh -c 作为没有参数的子进程调用,并且CMD 会丢失。
Docker 文档中说“如果 ENTRYPOINT 是一个字符串,则 CMD 被忽略”可能比试图解释其中的微妙之处更清楚。