Docker exec quoting variables - bash

I'd like to know if there's a way to do this
Let's say the dockerfile contains this line, that specifies path of an executable
ENV CLI /usr/local/bin/myprogram
I'd like to be able to call this program using ENV variable name through exec command.
For example
docker exec -it <my container> 'echo something-${CLI}
Expecting
something-/usr/local/bin/myprogram
However that returns:
OCI runtime exec failed: exec failed: container_linux.go:348: starting container process caused "exec: \"${CLI} do something\": executable file not found in $PATH": unknown

Ok, I found a way to do it, all you need to do is evaluate command with bash
docker exec -it <container id> bash -c 'echo something-${CLI}'
returns something-/usr/local/bin/myprogram
If the CLI environment variable is not already set in the container, you can also pass it in such as:
docker exec -it -e CLI=/usr/local/bin/myprogram <container id> bash -c 'echo something-${CLI}'
See the help file:
docker exec --help
Usage: docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
Run a command in a running container
Options:
-d, --detach Detached mode: run command in the background
-e, --env list Set environment variables
....

In it's original revision docker exec -it <my container> '${CLI} do something' with the expectation that ${CLI} will be substituted with /usr/local/bin/myprogram (as the exec COMMAND) and everything after passed as ARG's to /usr/local/bin/myprogram will not work, this is clearly documented: https://docs.docker.com/engine/reference/commandline/exec/
COMMAND should be an executable, a chained or a quoted command will not work. Example:
docker exec -ti my_container "echo a && echo b" will not work, but
docker exec -ti my_container sh -c "echo a && echo b" will.
Following the documentation, this will work as expected: docker exec -ti my_container sh -c "${CLI} foo", ${CLI} will be be executed after variable expansion and the argument(s) passed to the shell script set in ${CLI} (e.g. sh -c /usr/local/bin/myprogram foo).
Alternatively you could set the ENTRYPOINT to your script and pass in arguments with CMD or at the command line with docker run for example:
Given the below directory structure:
.
├── Dockerfile
└── example.sh
The Dockerfile contents:
FROM ubuntu:18.04
COPY example.sh /bin
RUN chmod u+x /bin/example.sh
ENTRYPOINT ["/bin/example.sh"]
CMD ["bla"]
And the example.sh script contents:
#!/bin/bash
echo $1
The CMD specified in the Dockerfile after the ENTRYPOINT will be the default argument for your script and you can override the default argument on the command line (assuming that the image is built and tagged as example:0.1):
user#host> docker run --rm example:0.1
bla
user#host> docker run --rm example:0.1 "arbitrary text"
arbitrary text
Note: this is my go to article for differences between ENTRYPOINT and CMD in Dockerfile's: https://medium.freecodecamp.org/docker-entrypoint-cmd-dockerfile-best-practices-abc591c30e21

Related

Εxecute commands with args and override entrypoint on docker run

I am trying to override the entrypoint in a docker image with a script execution that accepts arguments, and it fails as follows
▶ docker run --entrypoint "/bin/sh -c 'my-script.sh arg1 arg2'" my-image:latest
docker: Error response from daemon: OCI runtime create failed: container_linux.go:380: starting container process caused: exec: "/bin/sh -c 'myscript.sh arg1 arg2'": stat /bin/sh -c 'my-script.sh arg1 arg2': no such file or directory: unknown.
However when I exec to the container, the above command succeeds:
▶ docker run --entrypoint sh -it my-image:latest
~ $ /bin/sh -c 'my-script.sh arg1 arg2'
Success
Am I missing sth in the syntax?
Remember that arguments after the container image name are simply passed to the ENTRYPOINT script. So you can write:
docker run --entrypoint my-script.sh my-image:latest arg1 arg2
For example, if I have my-script.sh (mode 0755) containing:
#!/bin/sh
for arg in "$#"; do
echo "Arg: $arg"
done
And a Dockerfile like this:
FROM docker.io/alpine:latest
COPY my-script.sh /usr/local/bin/
ENTRYPOINT ["date"]
Then I can run:
docker run --rm --entrypoint my-script.sh my-image arg1 arg2
And get as output:
Arg: arg1
Arg: arg2
If you want to run an arbitrary sequence of shell commands, you can of course do this:
docker run --rm --entrypoint sh my-image \
-c 'ls -al && my-script.sh arg1 arg2'
If you need to do this at all regularly, you can refactor your Dockerfile to make this easier to do.
A Docker container's main process is run by concatenating together the "entrypoint" and "command" argument lists. In a Dockerfile, these come from the ENTRYPOINT and CMD directives. In the docker run command this is trickier: anything after the image name is the "command" part, but the "entrypoint" part needs to be provided by the --entrypoint argument, it needs to be before the image name, and it can only be a single word.
If you need to routinely replace the command, the syntax becomes much cleaner if you set it using CMD and not ENTRYPOINT in the Dockerfile.
# Dockerfile
CMD ["some", "main", "command"] # not ENTRYPOINT
If you make this change, then you can just put your alternate command after the image name in the docker run command, without a --entrypoint option and without splitting the command string around the image name.
docker run my-image:latest /bin/sh -c 'my-script.sh arg1 arg2'
I will somewhat routinely recommend a pattern where ENTRYPOINT is a wrapper script that does some first-time setup, then does something like exec "$#" to run the command that's passed to it as arguments. That setup is compatible with this CMD-first approach: the entrypoint wrapper will do its setup and then run the override command instead of the image's command.

docker run entrypoint with multiple commands

How can I have an entrypoint in a docker run which executes multiple commands?
Something like:
docker run --entrypoint "echo 'hello' && echo 'world'" ... <image>
The image I'm trying to run, has already an entrypoint set in the Dockerfile, so solution like the following seems not to work, because it looks my commands are ignored, and only the original entrypoint is executed
docker run ... <image> bash -c "echo 'hello' && echo 'world'"
In my use-case I must use the docker run command. Solution which change the Dockerfile are not acceptable, since it is not in my hands
As a style point, this gets vastly easier if your image has a CMD that can be overridden. If you only need to run one command with no initial setup, make it be the CMD and not the ENTRYPOINT:
CMD ./some_command # not ENTRYPOINT
If you need to do some initial setup and then launch the main command, make the ENTRYPOINT be a shell script that ends with the special instruction exec "$#". The CMD will be passed into it as parameters, and this line replaces the shell script with that command.
#!/bin/sh
# entrypoint.sh
... do first time setup, run database migrations, set variables ...
exec "$#"
# Dockerfile
...
ENTRYPOINT ["./entrypoint.sh"] # MUST be JSON-array syntax
CMD ./some_command # as before
If you do these things, then you can use your initial docker run form. This will replace the CMD but leave the ENTRYPOINT intact. In the wrapper-script case, your alternate command will be run as the exec "$#" command, so all of the first-time setup will be done first.
# Assuming the image correctly honors the CMD
docker run ... \
image-name \
sh -c 'echo "foo is $FOO" && echo "bar is $BAR"'
If you really can't do this, you can override the docker run --entrypoint. This runs instead of the image's entrypoint (if you want the image's entrypoint you have to run it yourself), and the syntax is awkward:
# Run a shell command instead of the entrypoint
docker run ... \
--entrypoint /bin/sh \
image-name \
-c 'echo "foo is $FOO" && echo "bar is $BAR"'
Note that the --entrypoint option comes before the image name, and its arguments come after the image name.

How to run a bash terminal in a Docker container along with additional commands?

To run a bash terminal in a Docker container I can run the following:
$ docker exec -it <container> /bin/bash
However, I want to execute a command in the container automatically. For example, if I want to open a bash terminal in the container and create a file I would expect to run something like:
docker exec -it <container> /bin/bash -c "touch foo.txt"
However, this doesn't work... Is there a simple way to achieve this? Of course, I could type the command after opening the container, but I want to open a bash terminal and run a command at the same time.
You can run your touch command and then spawn another shell :
docker exec -it <container> /bin/bash -c "touch foo.txt; exec bash"
Works perfectly fine for me
~# docker run -tid --rm --name test ubuntu:20.04
~# docker exec -it test /bin/bash -c "touch /foo.txt"
~# docker exec -it test /bin/bash
root#b6b0efbb13be:/# ls -ltr foo.txt
-rw-r--r-- 1 root root 0 Mar 7 05:35 foo.txt
Easy solution:
docker exec -it <container> touch foo.txt
You can verify
docker exec -it <container> ls
This was tested with alpine image.
Remember that in docker images there is a entrypoint and a command. Now we are editing the command of the default entrypoint for alpine, via docker exec
It depends of the entrypoint if env variablers are load or not, $PATH ..., so other images maybe you need to write /bin/touch or /usr/bin/ls
Good luck!
When you run docker exec -it <container> /bin/bash -c "touch foo.txt", container sends 0 exit code so that it means the task is done and you'll be returned to your host.
When you run docker exec -it <container /bin/bash, bash shell is not terminated until you explicitly type exit or use CTRL+D in bash environment. bash is continuously running.
This is why when you run the second command, it goes to bash, runs your command (touches) and then exits.

Docker starting container process caused "exec: \"arg\": executable file not found in $PATH": unknown.

My task is to create Dockerfile such that it works the following way:
docker build -t test .
Returns Image named test successfully created
docker run --rm test
Returns Hello world!
docker run --rm test Universe
Returns Hello Universe!
What I have so far:
Dockerfile
FROM ubuntu:14.04
LABEL maintainer="trthhrtz"
CMD if [ $# -eq 0 ]; then echo "Hello world!"; else echo "Hello " + $# + "!"; fi
It does not work in case of input arguments, the error is:
docker: Error response from daemon: OCI runtime create failed: container_linux.go:348: starting container process caused "exec: \"Universe\": executable file not found in $PATH": unknown.
ERRO[0000] error waiting for container: context canceled
It would be easier to:
define an entrypoint script entrypoint.sh with your command logic scripted in it.
COPY that file in your Dockerfile
leave CMD undefined
That way, any additional parameter to your docker run -it --rm myImage arg1 arg2 ... command will be passed to the bash entrypoint.sh script, which will interpret $# correctly, as illustrated in "What does set -e and exec "$#" do for docker entrypoint scripts?".
See "Passing arguments from CMD in docker" for more.
Make sure you have a correct order of arguments when you do docker command.
For example:
docker run --name test-ubuntu -it d37f4165b5d2 bash
instead of
docker run --name test-ubuntu d37f4165b5d2 -it bash

Docker exec Requires minimum of 2 arguments

I am using a shell script on Linux in order to execute some Docker commands :
docker exec -t -i test1 passwd
...
docker exec -t -i test2 passwd
And on the second exec command I receive the following error :
docker: "exec" requires a minimum of 2 arguments.
What am I doing wrong, or what am I missing?
Thank you in advance.
I have had the same mistake
docker exec -it gallant_bose
C:\Program Files\Docker Toolbox\docker.exe: "exec" requires a minimum of 2 arguments.
See 'C:\Program Files\Docker Toolbox\docker.exe exec --help'.
Usage: docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
Run a command in a running container
The solution, add the command bash in my case:
$ docker exec -it gallant_bose bash
root#e747ffecc84d:/#
Best wishes!
Update
Also, you can execute docker exec -it gallant_bose /bin/bash for some images
Are you sure that test2 exists?
I don't see any error in your command. If problem persists, can you provide the docker ps and docker images output please?

Resources