DEV Community

Cover image for Constructing commands to run in Docker containers
Ákos Takács
Ákos Takács

Posted on • Edited on

Constructing commands to run in Docker containers

Intro

In this tutorial I will explain multiple ways you can use to define what you want to run in the container when you start the container. You may think it is easy, since you know you can use the docker run command on your host and as an argument pass the command that has to run in the container. You also know you can use the CMD instruction in the Dockerfile, but this is not the full story, so let's see our options.

Note: The tutorial requires jq and recent Bash version installed on your machine.

If you want to be notified about other videos, you can subscribe to my channel:

https://www.youtube.com/@akos.takacs

Table of contents

Important facts

» Back to table of contents «

Before we begin running containers let's learn about the facts this tutorial was built around.

  • The actual command running in the container is based on the SHELL, ENTRYPOINT and the CMD instructions in the Dockerfile.
  • Commands defined as arguments of the RUN instruction will run during the build and never in the container started from the final image.
  • The RUN, ENTRYPOINT and the CMD instructions can be written in the exec form and the shell form.
  • The arguments of the CMD or instruction will be the arguments of the command defined as
    • an ENTRYPOINT if that exists and CMD is written in the exec form.
    • a SHELL if that exists and CMD is written in the shell form.
  • shell form means we define the command as a string and exec form means we define the command as a json list like ["echo", "Hello"].
  • The SHELL instruction must be written in the exec form as there is no other shell to which you could pass the command defined as a shell.
  • Passing commands as an argument to docker run is always equivalent to writing the CMD instruction in the exec form so the command will always be the argument of the entrypoint and never the shell.
  • If you don't have a shell, variables are not evaluated, so echo $HOME would just show $HOME on the console and the value of the variable.
  • Using docker run allows you to use variables which will be evaluated outside the container and not inside.
  • Even though passing commands to docker run is a kind of exec form, you can manually defined the shell in that form as an argument of docker run.
  • The command defined as a SHELL doesn't actually have to be a shell. It can be any command.

Examples

Example 1 - CMD in exec form

» Back to table of contents «

We need to create a simple Dockerfile called Dockerfile.v1 like this:

FROM ubuntu:22.04

CMD ["echo", "$HOME"]
Enter fullscreen mode Exit fullscreen mode

Let's build the image and run the container.

docker build . --force-rm -f Dockerfile.v1 -t localhost/command:v1
docker run -i --name "command-v1-0" "localhost/command:v1"
Enter fullscreen mode Exit fullscreen mode

And the output is

$HOME
Enter fullscreen mode Exit fullscreen mode

We basically told Docker to execute echo $HOME without evaluating the variable $HOME. If you want to get the same result in your terminal interactively, you have to use apostrophes around each part of the command.

'echo' '$HOME'
Enter fullscreen mode Exit fullscreen mode

Since echo is the same without apostrophes, you could just run the following:

echo '$HOME'
Enter fullscreen mode Exit fullscreen mode

So the statement that the exec form will not evaluate variables is proven, but the interactive examples doesn't prove that we don't have a shell. Let's ask Docker what command it actually ran.

Note: The jq part is required only because the original output would container a quoted string.

docker container ls -a --no-trunc --format '{{ .Command }}' --filter 'name=^command-v1-0$' \
  | jq --raw-output
Enter fullscreen mode Exit fullscreen mode

It shows that the actual command running in the container is

echo 'My home is $HOME'
Enter fullscreen mode Exit fullscreen mode

In some shells like zshell echo is a built-in command, but in bash in our container it is at /usr/bin/echo. You can check this by running the following using the base image:

docker run -it --rm ubuntu:22.04 which echo
Enter fullscreen mode Exit fullscreen mode

So Docker just ran the binary instead of using a shell. Now let's pass the command as an argument to docker run.

docker run -i --name "command-v1-1" "localhost/rimelek/tutorial-docker-command:v1" \
  echo "My home is $HOME"
Enter fullscreen mode Exit fullscreen mode

In this case the output on my machine is

My home is /Users/ta
Enter fullscreen mode Exit fullscreen mode

because my home dir on Mac is indeed /Users/ta and the variable was interpreted on the host, not in the container. This is the only reason it worked, but it could not use a variable this way inside the container.

And finally, check the command that ran in the container:

docker container ls -a --no-trunc --format '{{ .Command }}' --filter 'name=^command-v1-1$' \
  | jq --raw-output
Enter fullscreen mode Exit fullscreen mode

Output:

echo 'My home is /Users/ta'
Enter fullscreen mode Exit fullscreen mode

Example 2 - CMD in shell form as a single argument

» Back to table of contents «

Now we will try the shell form in a CMD instruction. We have actually two ways to do this. First we will quote the entire command as a single argument of the CMD instruction. The content of Dockerfile.v2 is the following:

FROM ubuntu:22.04

CMD "echo \"My home is $HOME\""
Enter fullscreen mode Exit fullscreen mode
docker build . --force-rm -f Dockerfile.v2 -t localhost/rimelek/tutorial-docker-command:v2
docker run -i --name "command-v2-0" "localhost/rimelek/tutorial-docker-command:v2"
Enter fullscreen mode Exit fullscreen mode

It will show you the folowing error message:

/bin/sh: 1: echo "My home is /root": not found
Enter fullscreen mode Exit fullscreen mode

The fact is that the CMD instruction's argument is not just what you quote but it also includes the quote itself. It means that echo "My home is $HOME" was interpreted as a single executable filename which couldn't be found obsiously. Let's find out what the command was in the container:

docker container ls -a --no-trunc --format '{{ .Command }}' --filter 'name=^command-v2-0$' \
  | jq --raw-output
Enter fullscreen mode Exit fullscreen mode

Output:

/bin/sh -c '"echo \"My home is $HOME\""'
Enter fullscreen mode Exit fullscreen mode

If you try it with an argument passed to docker run, as I mentioned at the beginning of the tutorial, we will actually use the exec form, so what happens then?

docker run -i --name "command-v2-1" "localhost/rimelek/tutorial-docker-command:v2" \
  "echo \"My home is $HOME\""
Enter fullscreen mode Exit fullscreen mode

We will get an error message, but in this case not from the process inside the container because the exec form will be interpreted by Docker itself:

docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "echo \"My home is /Users/ta\"": stat echo "My home is /Users/ta": no such file or directory: unknown.
time="2023-07-02T13:12:52+02:00" level=error msg="error waiting for container: "
Enter fullscreen mode Exit fullscreen mode

The environment variable was evaluated, but Docker couldn't find "echo \"My home is $HOME\"" as a single executable file, so it could not start the container, therefore we don't have any output. You can run

docker logs command-v2-1
Enter fullscreen mode Exit fullscreen mode

to confirm it. In any other case when Docker can start the container the error will be thrown by the the process inside the container wich will generate logs. You could check the running command in the container, but it won't be different from a working command because the original quotation mark will be lost in the output:

docker container ls -a --no-trunc --format '{{ .Command }}' --filter 'name=^command-v2-1$' \
  | jq --raw-output
Enter fullscreen mode Exit fullscreen mode
echo "My home is /Users/ta"
Enter fullscreen mode Exit fullscreen mode

Example 3 - CMD in shell form as multiple arguments

» Back to table of contents «

Our new Dockerfile will be Dockerfile.v3:

FROM ubuntu:22.04

CMD echo "My home is $HOME"
Enter fullscreen mode Exit fullscreen mode

Now we still use the CMD instruction, but in this case we don't quote the value as a single argument.

docker build . --force-rm -f Dockerfile.v3 -t localhost/rimelek/tutorial-docker-command:v3
docker run -i --name "command-v3-0" "localhost/rimelek/tutorial-docker-command:v3"
Enter fullscreen mode Exit fullscreen mode

The output now shows the value of the envrionment variable $HOME inside the container:

My home is /root
Enter fullscreen mode Exit fullscreen mode

It works, because now we used a correct shell form. We check the command inside the container:

docker container ls -a --no-trunc --format '{{ .Command }}' --filter 'name=^command-v3-0$' \
  | jq --raw-output
Enter fullscreen mode Exit fullscreen mode

and the output is:

/bin/sh -c 'echo "My home is $HOME"'
Enter fullscreen mode Exit fullscreen mode

It clearly shows that the argument of the CMD instruction became the argument of /bin/sh -c which is the default value of the SHELL instruction in the Dockerfile. Passing the command as an argument of docker run would again evaluate the variable on the host:

docker run -i --name "command-v3-1" "localhost/rimelek/tutorial-docker-command:v3" \
  echo "My home is $HOME"
Enter fullscreen mode Exit fullscreen mode

Output:

My home is /Users/ta
Enter fullscreen mode Exit fullscreen mode

Check the running command inside the container:

docker container ls -a --no-trunc --format '{{ .Command }}' --filter 'name=^command-v3-1$' \
  | jq --raw-output
Enter fullscreen mode Exit fullscreen mode

Output:

echo 'My home is /Users/ta'
Enter fullscreen mode Exit fullscreen mode

Example 4 - CMD in shell form running a shell script without a shebang line

» Back to table of contents «

The next example using Dockerfile.v4 shows how you can use shell scripts with the CMD instruction.

FROM ubuntu:22.04

COPY src/hello.sh /

# COPY --chmod is supported with buildkit instead of the below instruction
RUN chmod +x /hello.sh

CMD /hello.sh
Enter fullscreen mode Exit fullscreen mode

Note that we also use a RUN instruction written in the shell form which is what we usually do as opposed to how we use the CMD instruction in which we usually use the exec form, but during the build process we often need a shell to use variables and pipe run a chain of commands. In this example we will stick to the shell form in CMD, so hello.sh will be the argument of the default shell again, which is /bin/sh -c.

docker build . --force-rm -f Dockerfile.v4 -t localhost/rimelek/tutorial-docker-command:v4
docker run -i --name "command-v4-0" "localhost/rimelek/tutorial-docker-command:v4"
Enter fullscreen mode Exit fullscreen mode

The output:

Hello Docker
Enter fullscreen mode Exit fullscreen mode

Let's check the content of hello.sh and how we got this output:

name="${*:-Docker}"

echo "Hello $name"
Enter fullscreen mode Exit fullscreen mode

You probably noticed that there is no shebang line (#!/bin/bash) at the beginning of the file. Although many shell script has one, it is not required if you always want to pass the file to a specific shell as argument. And this is what happens using the shell form.

Check the command running in the container:

docker container ls -a --no-trunc --format '{{ .Command }}' --filter 'name=^command-v4-0$' \
  | jq --raw-output
Enter fullscreen mode Exit fullscreen mode

Output

/bin/sh -c /hello.sh
Enter fullscreen mode Exit fullscreen mode

You know what's coming, we will use the exec form with the Docker CLI:

docker run -i --name "command-v4-0" "localhost/rimelek/tutorial-docker-command:v4" /hello.sh
Enter fullscreen mode Exit fullscreen mode

Remember, in this case there is no shell involved automatically, so the comman will fail:

exec /hello.sh: exec format error
Enter fullscreen mode Exit fullscreen mode

Check the running command in the container:

docker container ls -a --no-trunc --format '{{ .Command }}' --filter 'name=^command-v4-1$' \
  | jq --raw-output
Enter fullscreen mode Exit fullscreen mode

The output:

/hello.sh
Enter fullscreen mode Exit fullscreen mode

So hello.sh was executaed without the shebang line and without any manually provided shell. That will not work. We will get back to this later.

Example 5 - Use anything as shell

» Back to table of contents «

The 5th example shows that we can override the default shell and that doesn't even have to be an actuall shell. That perfectly demonstrates that the SHELL, ENTRYPOINT and CMD instructions are all about what will be the argument of what and knowing that you can do whatever you want with this information.

Our Dockerfile.v5 is the following:

FROM ubuntu:22.04

COPY src/hello.sh /

RUN chmod +x /hello.sh

SHELL ["/bin/ls", "-l"]

CMD /hello.sh
Enter fullscreen mode Exit fullscreen mode

As funny as it is, we will use the ls command in the SHELL instruction which will take hello.sh as an argument. You can guess that it will just show the file path when we start the container, but let's build the image.

docker build . --force-rm -f Dockerfile.v5 -t localhost/rimelek/tutorial-docker-command:v5
docker run -i --name "command-v5-0" "localhost/rimelek/tutorial-docker-command:v5"
Enter fullscreen mode Exit fullscreen mode

The output is:

-rwxr-xr-x 1 root root 40 Jun 20 18:05 /hello.sh
Enter fullscreen mode Exit fullscreen mode

Check what the running command was:

docker container ls -a --no-trunc --format '{{ .Command }}' --filter 'name=^command-v5-0$' \
  | jq --raw-output
Enter fullscreen mode Exit fullscreen mode

Output:

/bin/ls -l /hello.sh
Enter fullscreen mode Exit fullscreen mode

So it's time to use the exec form with the Docker CLI:

docker run -i --name "command-v5-1" "localhost/rimelek/tutorial-docker-command:v5" /hello.sh
Enter fullscreen mode Exit fullscreen mode

It fails because the ls command was the shell and not the entrypoint.

Here comes the command checking:

docker container ls -a --no-trunc --format '{{ .Command }}' --filter 'name=^command-v5-1$' \
  | jq --raw-output
Enter fullscreen mode Exit fullscreen mode

Output

hello.sh
Enter fullscreen mode Exit fullscreen mode

Nothing surprising.

Example 6 - Shell script without shebang line as entrypoint in shell form

» Back to table of contents «

Since in the previous example the problem was the entrypoint, let's use one in Dockerfile.v6:

FROM ubuntu:22.04

COPY src/hello.sh /

RUN chmod +x /hello.sh

SHELL ["/bin/bash", "-c"]

ENTRYPOINT /hello.sh

CMD my friend
Enter fullscreen mode Exit fullscreen mode

Build the image and run the container:

docker build . --force-rm -f Dockerfile.v6 -t localhost/rimelek/tutorial-docker-command:v6
docker run -i --name "command-v6-0" "localhost/rimelek/tutorial-docker-command:v6"
Enter fullscreen mode Exit fullscreen mode

The output is:

Hello Docker
Enter fullscreen mode Exit fullscreen mode

Shouldn't the CMD be the agument of the entrypoint? Why did we get "Hello Docker" instead of "Hello my friend"? Let's check what was running in the container:

docker container ls -a --no-trunc --format '{{ .Command }}' --filter 'name=^command-v6-0$' \
  | jq --raw-output
Enter fullscreen mode Exit fullscreen mode

Output:

/bin/bash -c /hello.sh /bin/bash -c 'my friend'
Enter fullscreen mode Exit fullscreen mode

Since we wrote the ENTRYPOINT instruction in the shell form, it behaved exactly as the CMD would have worked. It became the argument of the shell which changed the original entrypoint to the following:

/bin/bash -c /hello.sh
Enter fullscreen mode Exit fullscreen mode

Then we also wrote the CMD instruction in shell form so the new command became:

/bin/bash -c 'my friend'
Enter fullscreen mode Exit fullscreen mode

It seems because everything is in shell form the modified command could become the argument of the modified entrypoint, but it doesn't make sense, since the bash shell does not take any more arguments when we define the shell script as a command instead of a script file, so everything after -c hello.sh will be ignored without throwing any error message. It isn't a big issue, since the ENTRYPOINT instruction is usually used in the exec form, but what happens if we pass the string "my friend" to docker run as an argument.

docker run -i --name "command-v6-1" "localhost/rimelek/tutorial-docker-command:v6" my friend
Enter fullscreen mode Exit fullscreen mode

Output:

Hello Docker
Enter fullscreen mode Exit fullscreen mode

Something is still wrong, so let's check the command that was running in the container:

docker container ls -a --no-trunc --format '{{ .Command }}' --filter 'name=^command-v6-1$' \
  | jq --raw-output
Enter fullscreen mode Exit fullscreen mode

Output:

/bin/bash -c /hello.sh my friend
Enter fullscreen mode Exit fullscreen mode

"my friend" is still an ignored argument of of the bash shell, since we left the entrypoint in the shell form.

Example 7 - Shell script without shebang line as entrypoint in exec form

» Back to table of contents «

In this example with the help of Dockerfile.v7 we will fix the entrypoint and the command to use the exec form as recommended.

FROM ubuntu:22.04

COPY src/hello.sh /

RUN chmod +x /hello.sh

SHELL ["/bin/bash", "-c"]

ENTRYPOINT ["/hello.sh"]

CMD ["my", "friend"]
Enter fullscreen mode Exit fullscreen mode

Let's build the image and start the container:

docker build . --force-rm -f Dockerfile.v7 -t localhost/rimelek/tutorial-docker-command:v7
docker run -i --name "command-v7-0" "localhost/rimelek/tutorial-docker-command:v7"
Enter fullscreen mode Exit fullscreen mode

And now we are back to the previous problem. We still don't have a shebang line in the script, so we get the error message:

exec /hello.sh: exec format error
Enter fullscreen mode Exit fullscreen mode

This time my friend became the argument of hello.sh properly, but hello.sh still can't be interpreted because of the lack of the shebang line or any provided shell.

Check the command that resulted the error:

docker container ls -a --no-trunc --format '{{ .Command }}' --filter 'name=^command-v7-0$' \
  | jq --raw-output
Enter fullscreen mode Exit fullscreen mode

Output

/hello.sh my friend
Enter fullscreen mode Exit fullscreen mode

Even if we pass "my freind" as an argument of docker run it wont change anything, since we already used
the exec form.

docker run -i --name "command-v7-1" "localhost/rimelek/tutorial-docker-command:v7" my friend
Enter fullscreen mode Exit fullscreen mode
exec /hello.sh: exec format error
Enter fullscreen mode Exit fullscreen mode
docker container ls -a --no-trunc --format '{{ .Command }}' --filter 'name=^command-v7-1$' \
  | jq --raw-output
Enter fullscreen mode Exit fullscreen mode

Output

/hello.sh my friend
Enter fullscreen mode Exit fullscreen mode

Example 8 - Shell script with shebang line executing the CMD

» Back to table of contents «

We need to use a new script with a shebang line that defines the shell in the first line. The new Dockerfile called Dockerfile.v8 will make sure the new hello-bash.sh will be copied:

FROM ubuntu:22.04

COPY src/hello-bash.sh /

RUN chmod +x /hello-bash.sh

SHELL ["/bin/bash", "-c"]

ENTRYPOINT ["/hello-bash.sh"]

CMD ["whoami"]
Enter fullscreen mode Exit fullscreen mode

The content of the new shell script is:

#!/bin/bash

"$@"
Enter fullscreen mode Exit fullscreen mode

It will let us define a command as an argument that has to be executaed in the script. Let's run the first container with this script:

docker build . --force-rm -f Dockerfile.v8 -t localhost/rimelek/tutorial-docker-command:v8
docker run -i --name "command-v8-0" "localhost/rimelek/tutorial-docker-command:v8"
Enter fullscreen mode Exit fullscreen mode

The output:

root
Enter fullscreen mode Exit fullscreen mode

And check the command that was running on the container:

docker container ls -a --no-trunc --format '{{ .Command }}' --filter 'name=^command-v8-0$' \
  | jq --raw-output
Enter fullscreen mode Exit fullscreen mode

Output:

/hello-bash.sh whoami
Enter fullscreen mode Exit fullscreen mode

So even by using the exec form, you can have a shell script if you have a shell defined in the first line.

Note: You would probably expect /bin/bash -c '/hello-bash.sh whoami' to be the actual running command in the container since I stated that the command worked because there was a shell. Your expectation is almost correct, but we can't get that command from the metadata given us by docker container ls. Up until now everything could be determined based on the content of the Dockerfile, but the shebang line is not known directly by Docker and in this case the shell script wasn't passed to the shell defined in the Dockerfile, so the final command will be different.

Let's pass the executable command as an argument of docker run:

docker run -i --name "command-v8-1" "localhost/rimelek/tutorial-docker-command:v8" whoami
Enter fullscreen mode Exit fullscreen mode

The output is still just root and the final command is still the same too.

docker container ls -a --no-trunc --format '{{ .Command }}' --filter 'name=^command-v8-1$' \
  | jq --raw-output
Enter fullscreen mode Exit fullscreen mode

Output:

/hello-bash.sh whoami
Enter fullscreen mode Exit fullscreen mode

If you change the command to be ps 1 you can get the running process from the container's point of view:

docker run --rm  "localhost/rimelek/tutorial-docker-command:v8" ps 1
Enter fullscreen mode Exit fullscreen mode

Output:

  PID TTY      STAT   TIME COMMAND
    1 ?        Ss     0:00 /bin/bash /hello-bash.sh ps 1
Enter fullscreen mode Exit fullscreen mode

And now we have the bash shell and its aguments. It executes /hello-bash.sh and since we didn't use the -c option to define a command as a string, it won't ignore the rest of the arguments but recognizes the shell script as a file and passes the rest of the arguments to it.

Example 9 - Using a fake shell to make a file executable

» Back to table of contents «

At the beginning of this post I mentioned that we can also write the RUN instruction in two different forms and we already used it in the shell form. Depending on what the shell is, you might want to write it in the exec form, but first let's keep the shell form what we usually use. Since we don't really want to run the script during build, we will also change the shell again to chmod so instead of running it in a shell we will make it executable this way.

FROM ubuntu:22.04

COPY src/hello-bash.sh /

SHELL ["chmod", "+x"]

RUN /hello-bash.sh

SHELL ["/bin/bash", "-c"]

ENTRYPOINT ["/hello-bash.sh"]

CMD ["whoami"]
Enter fullscreen mode Exit fullscreen mode

Build and run the container:

docker build . --force-rm -f Dockerfile.v9 -t localhost/rimelek/tutorial-docker-command:v9
docker run -i --name "command-v9-0" "localhost/rimelek/tutorial-docker-command:v9"
Enter fullscreen mode Exit fullscreen mode

The output is

root
Enter fullscreen mode Exit fullscreen mode

Note that this example is just preparing ourself to the next one, so it won't give us any interesting output, but since we don't have an error message, we can be sure that the build ran correctly and we could also run a container from it. Let's see the command inside the container.

docker container ls -a --no-trunc --format '{{ .Command }}' --filter 'name=^command-v9-0$' \
  | jq --raw-output
Enter fullscreen mode Exit fullscreen mode

Output:

/hello-bash.sh whoami
Enter fullscreen mode Exit fullscreen mode

And now passing whoami as an argument to docker run:

docker run -i --name "command-v9-1" "localhost/rimelek/tutorial-docker-command:v9" whoami
Enter fullscreen mode Exit fullscreen mode

The output is still root and the running command inside the container didn't change either

docker container ls -a --no-trunc --format '{{ .Command }}' --filter 'name=^command-v9-1$' \
  | jq --raw-output
Enter fullscreen mode Exit fullscreen mode

Output:

/hello-bash.sh whoami
Enter fullscreen mode Exit fullscreen mode

Example 10 - Failing RUN instruction in exec form

» Back to table of contents «

Now we arrived to the example which will show what happens when you use the RUN instruction in the exec form. Our Dockefile is Dockerfile.v10.

FROM ubuntu:22.04

COPY src/hello-bash.sh /

SHELL ["chmod", "+x"]

RUN ["/hello-bash.sh"]

SHELL ["/bin/bash", "-c"]

ENTRYPOINT ["/hello-bash.sh"]

CMD ["whoami"]
Enter fullscreen mode Exit fullscreen mode

Build the image

docker build . --force-rm -f Dockerfile.v10 -t localhost/rimelek/tutorial-docker-command:v10
Enter fullscreen mode Exit fullscreen mode

Now it will show an error message, because the /hello-bash.sh in the RUN instruction wasn't passed to the shell, because it was in the exec form. The entrypoint has nothing to do with the RUN instruction so we won't end up with an inficnite loop.

The error message is the following:

#7 [3/3] RUN ["/hello-bash.sh"]
#0 0.081 runc run failed: unable to start container process: exec: "/hello-bash.sh": permission denied
#7 ERROR: process "/hello-bash.sh" did not complete successfully: exit code: 1
------
 > [3/3] RUN ["/hello-bash.sh"]:
#0 0.081 runc run failed: unable to start container process: exec: "/hello-bash.sh": permission denied
------
Dockerfile.v10:7
--------------------
   5 |     SHELL ["chmod", "+x"]
   6 |
   7 | >>> RUN ["/hello-bash.sh"]
   8 |
   9 |     SHELL ["/bin/bash", "-c"]
--------------------
ERROR: failed to solve: process "/hello-bash.sh" did not complete successfully: exit code: 1
Enter fullscreen mode Exit fullscreen mode

Example 11 - Working RUN instruction in exec form

» Back to table of contents «

The last example will show a working version again, but in this case although we still write the RUN instruction in the exec form, we write it correctly containing the actual chmod command instead of relying on the SHELL. Let's see the content of Dockerfile.v11.

FROM ubuntu:22.04

COPY src/hello-bash.sh /

RUN ["chmod", "+x", "/hello-bash.sh"]

SHELL ["/bin/bash", "-c"]

ENTRYPOINT ["/hello-bash.sh"]

CMD ["whoami"]
Enter fullscreen mode Exit fullscreen mode

Build and run:

docker build . --force-rm -f Dockerfile.v11 -t localhost/rimelek/tutorial-docker-command:v11
docker run -i --name "command-v11-0" "localhost/rimelek/tutorial-docker-command:v11"
Enter fullscreen mode Exit fullscreen mode

The output is just root as in the previously working examples, but this RUN instruction wouldn't work if you wanted to use an environment variable in the filename for example, because that would work in a shell only. Without expecting any surprise, let's check the command in the container:

docker container ls -a --no-trunc --format '{{ .Command }}' --filter 'name=^command-v11-0$' \
  | jq --raw-output
Enter fullscreen mode Exit fullscreen mode

Output:

/hello-bash.sh whoami
Enter fullscreen mode Exit fullscreen mode

And you would get the same results with the following commands as well:

docker run -i --name "command-v11-1" "localhost/rimelek/tutorial-docker-command:v11" whoami
Enter fullscreen mode Exit fullscreen mode
docker container ls -a --no-trunc --format '{{ .Command }}' --filter 'name=^command-v11-1$' \
  | jq --raw-output
Enter fullscreen mode Exit fullscreen mode

Output:

/hello-bash.sh whoami
Enter fullscreen mode Exit fullscreen mode

Conclusion

» Back to table of contents «

I hope you now you understand the instructions a little bit better. I used the Docker commands directly so you can copy and paste safely without worrying about what unexpected commands in a script, but you can also clone the Git repository from GitHub and use the scripts mentioned in the readme to have a colored, automatically executed test which you can run as many times as you want and use ./reset.sh to remove all the generated images and containers.

GitHub logo rimelek / tutorial-docker-command

Scripts to demonstrate the ways to build the main process inside a Docker container

Building the main process inside Docker container

This source code was created for a Hungarian Youtube tutorial For english links, please check the release list or the latest version of this readme It demonstrates how you can build the main process (the first process with 1 as PID) inside a Docker container using SHELL, ENTRYPOINT and CMD and how the RUN instruction is similar to the others.

Note: The tutorial requires jq and recent Bash version installed on your machine.

Each Dockerfile has a version number. You can run the tests one by one using build.sh and run.sh passing the version as the first argument to each script.

./build.sh v1
./run.sh v1
Enter fullscreen mode Exit fullscreen mode

Pass a command optionally as the second argument

./run.sh v1 'echo "Hello Docker"'
Enter fullscreen mode Exit fullscreen mode

Run all tests using test-all.sh

./test-all.sh
Enter fullscreen mode Exit fullscreen mode

You can also list the already created containers:

./list.sh
Enter fullscreen mode Exit fullscreen mode

Or delete everything with reset.sh

./reset.sh
Enter fullscreen mode Exit fullscreen mode

NOTE

After cloning the project you might want to switch to v2.0.0 in case there are newer commits:

git checkout v2.0.0
Enter fullscreen mode Exit fullscreen mode

The output of ./test-all.sh would be something like this but colored (use the horizontal scrollbar to see the right side of the output):

Container      Command                                          Output
command-v1-0   echo 'My home is $HOME'                          My home is $HOME
command-v1-1   echo 'My home is /Users/ta'                      My home is /Users/ta
command-v2-0   /bin/sh -c '"echo \"My home is $HOME\""'         /bin/sh: 1: echo "My home is /root": not found
command-v2-1   echo "My home is /Users/ta"
command-v3-0   /bin/sh -c 'echo "My home is $HOME"'             My home is /root
command-v3-1   echo 'My home is /Users/ta'                      My home is /Users/ta
command-v4-0   /bin/sh -c /hello.sh                             Hello Docker
command-v4-1   /hello.sh                                        exec /hello.sh: exec format error
command-v5-0   /bin/ls -l /hello.sh                             -rwxr-xr-x 1 root root 40 Jun 20 18:05 /hello.sh
command-v5-1   /hello.sh                                        exec /hello.sh: exec format error
command-v6-0   /bin/bash -c /hello.sh /bin/bash -c 'my friend'  Hello Docker
command-v6-1   /bin/bash -c /hello.sh my friend                 Hello Docker
command-v7-0   /hello.sh my friend                              exec /hello.sh: exec format error
command-v7-1   /hello.sh my friend                              exec /hello.sh: exec format error
command-v8-0   /hello-bash.sh whoami                            root
command-v8-1   /hello-bash.sh whoami                            root
command-v9-0   /hello-bash.sh whoami                            root
command-v9-1   /hello-bash.sh whoami                            root
command-v10-0
command-v10-1
command-v11-0  /hello-bash.sh whoami                            root
command-v11-1  /hello-bash.sh my friend                         Hello my friend
Enter fullscreen mode Exit fullscreen mode

Top comments (0)