<p>Here is a sample dockerfile with a shell-form entrypoint:</p>
<pre><code>FROM ubuntu
ENTRYPOINT /bin/echo &quot;(shell) ENTRYPOINT@image ($0)&quot;
</code></pre>
<p>Below, are some of the outputs that I see from various runs:</p>
<pre><code>$ docker run -it sc-test:v4
(shell) ENTRYPOINT@image (/bin/sh)
$ docker run -it sc-test:v4 /bin/echo
(shell) ENTRYPOINT@image (/bin/echo)
$ docker run -it sc-test:v4 /bin/cat
(shell) ENTRYPOINT@image (/bin/cat)
$ docker run -it sc-test:v4 /bin/dog
(shell) ENTRYPOINT@image (/bin/dog)
$ docker run -it sc-test:v4 &quot;/bin/dog ($0)&quot;
(shell) ENTRYPOINT@image (/bin/dog (-bash))
</code></pre>
<p>Based on docker documentation <a href="https://docs.docker.com/engine/reference/builder/#understand-how-cmd-and-entrypoint-interact" rel="nofollow noreferrer">here</a>, we can see that the command args are ignored.</p>
<p>However, the value of <code>$0</code> changes with the args provided. Can someone explain why this happens? Thanks!</p>

<p>The table in that part of the Docker documentation isn't technically correct: Docker doesn't actually drop or ignore the command part when there's a shell-form entrypoint.  What actually happens here (and your example demonstrates) is:</p>
<ol>
<li>If either the <code>ENTRYPOINT</code> or <code>CMD</code> (or both) is the shell form, it's wrapped in <code>[&quot;/bin/sh&quot;, &quot;-c&quot;, &quot;...&quot;]</code>.</li>
<li>The <code>ENTRYPOINT</code> and <code>CMD</code> lists are concatenated to form a single command list.</li>
</ol>
<p>Let's take your third example.  This is, in Dockerfile syntax,</p>
<pre><code>ENTRYPOINT /bin/echo &quot;(shell) ENTRYPOINT@image ($0)&quot;
CMD [&quot;/bin/cat&quot;]
</code></pre>
<p>and the resulting combined command is (in JSON array syntax, expanded for clarity)</p>
<pre><code>[
  &quot;/bin/sh&quot;,
  &quot;-c&quot;,
  &quot;/bin/echo \&quot;(shell) ENTRYPOINT@image ($0)\&quot;&quot;,
  &quot;/bin/cat&quot;
]
</code></pre>
<p>So, what does <code>sh -c</code> do if you give it multiple arguments?  The <a href="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/sh.html" rel="noreferrer">POSIX spec for the <code>sh</code> command</a> documents the syntax as</p>
<pre><code>sh -c command_string [command_name [argument...]]
</code></pre>
<p>and further documents</p>
<blockquote>
<p><strong>-c</strong>: Read commands from the <em>command_string</em> operand. Set the value of special parameter 0 [...] from the value of the <em>command_name</em> operand and the positional parameters ($1, $2, and so on) in sequence from the remaining <em>argument</em> operands. No commands shall be read from the standard input.</p>
</blockquote>
<p>That's what you're seeing in your examples.  If <code>ENTRYPOINT</code> is a bare string and <code>CMD</code> is a JSON array, then within the <code>ENTRYPOINT</code> string command, the arguments in <code>CMD</code> can be used as <code>$0</code>, <code>$1</code>, and so on.  If both are bare strings, both get wrapped in <code>sh -c</code>, and you'll get something like:</p>
<pre class="lang-sh prettyprint-override"><code>ENTRYPOINT /bin/echo &quot;$0 is /bin/sh, $1 is -c, and then $2&quot;
CMD the rest of the line
</code></pre>
<p>In your first example, the command part is empty, and in this case (still from the POSIX <code>sh</code> documentation)</p>
<blockquote>
<p>If <em>command_name</em> is not specified, special parameter 0 shall be set to [...] normally a pathname used to execute the sh utility.</p>
</blockquote>
<p>Your last example is slightly more subtle:</p>
<pre class="lang-sh prettyprint-override"><code>docker run -it sc-test:v4 &quot;/bin/dog ($0)&quot;
</code></pre>
<p>Since the string is double-quoted, your <em>local</em> shell expands the <code>$0</code> reference in it, which is how <code>bash</code> gets in there; then since it's a single (quoted) word, it becomes the single <em>command_name</em> argument to <code>sh -c</code>.</p>
<hr />
<p>There's two more normal patterns for using <code>ENTRYPOINT</code> and <code>CMD</code> together.  The pattern I prefer has <code>CMD</code> be a full shell command, and <code>ENTRYPOINT</code> does some first-time setup and then runs a command like <code>exec &quot;$@&quot;</code> to run that command.  There's also a &quot;container as command&quot; pattern where <code>ENTRYPOINT</code> has a complete command (perhaps with involved JVM arguments) and <code>CMD</code> additional options.  In these cases the <code>ENTRYPOINT</code> <em>must</em> be JSON-array syntax:</p>
<pre><code>ENTRYPOINT [&quot;/script-that-exec-dollar-at.sh&quot;]
CMD [&quot;the_command&quot;, &quot;--option&quot;]
</code></pre>
<p>Since the <code>ENTRYPOINT</code> string doesn't <em>directly</em> reference <code>$0</code>, <code>$1</code>, <em>etc.</em> the <code>CMD</code> arguments are <em>effectively</em> ignored by the <code>sh -c</code> wrapper.  If you had <code>ENTRYPOINT script.sh</code> it would be invoked by <code>sh -c</code> as a subprocess with no arguments, and the <code>CMD</code> would get lost.</p>
<p>It's probably clearer for the Docker documentation to say &quot;if <code>ENTRYPOINT</code> is a string then <code>CMD</code> is ignored&quot; than to try to explain the subtleties of this.</p>
