357

I'm trying to run MULTIPLE commands like this.

docker run image cd /path/to/somewhere && python a.py

But this gives me "No such file or directory" error because it is interpreted as...

"docker run image cd /path/to/somewhere" && "python a.py"

It seems that some ESCAPE characters like "" or () are needed.

So I also tried

docker run image "cd /path/to/somewhere && python a.py"
docker run image (cd /path/to/somewhere && python a.py)

but these didn't work.

I have searched for Docker Run Reference but have not find any hints about ESCAPE characters.

mohan08p
  • 5,002
  • 1
  • 28
  • 36
ai0307
  • 4,037
  • 2
  • 13
  • 21
  • Note that for this particular use, `docker run` has a `-w/--workdir` argument. `docker run -w /path/to/somewhere image python a.py` – pullmyteeth Feb 25 '21 at 20:07

10 Answers10

576

To run multiple commands in docker, use /bin/bash -c and semicolon ;

docker run image_name /bin/bash -c "cd /path/to/somewhere; python a.py"

In case we need command2 (python) will be executed if and only if command1 (cd) returned zero (no error) exit status, use && instead of ;

docker run image_name /bin/bash -c "cd /path/to/somewhere && python a.py"
Govind Rai
  • 14,406
  • 9
  • 72
  • 83
anhlc
  • 13,839
  • 4
  • 33
  • 40
  • Thank you for your hint! I simply tried [ docker run image "cd /bin; ls" ] and got following error messages. [ exec: "cd /bin; ls": stat cd /bin; ls: no such file or directory ] [ Error response from daemon: Cannot start container ] – ai0307 Feb 13 '15 at 02:19
  • 1
    Anyone finds this answer might be interested in how to use here documents to feed commands to a container: [here's my answer to another question like this about here documents, feel free to check it out](http://stackoverflow.com/a/35767573/433835). – Kohányi Róbert Mar 03 '16 at 09:31
  • 24
    `docker run alpine sh -c "ls | wc >> xxx"` this help me a lot, when bash is not available. – Eddy Hernandez Jan 06 '17 at 20:37
  • In case someone is facing troubles when using a command with `$(...)` please have a look [here](https://serverfault.com/a/784225/411690) – ira Nov 08 '17 at 11:17
  • 1
    This works for `cd` but not for things where return codes matter `;` is not the same as `&&` see https://unix.stackexchange.com/questions/37069/what-is-the-difference-between-and-when-chaining-commands – Nick Zalutskiy May 31 '18 at 13:13
  • 1
    Thanks @NickZalutskiy, updated answer with your comment – anhlc May 31 '18 at 23:23
  • Any simple solution to use the same along with passing parameters to the second command? – Prasannjeet Singh Jan 23 '22 at 20:05
  • @anhlc Because the docker api for shell, golang and python have same functionality, how does those container native continuous integration service like drone-ci or gitlab-ci achieve this? – Audra Jacot Dec 07 '22 at 04:52
  • 2
    Nobody has mentioned that `docker run image_name /bin/bash -c` just appends a command to the entrypoint, and is not a silver bullet. Please check my answer below https://stackoverflow.com/a/75047712/20085654. – igops Jan 08 '23 at 12:27
36

You can do this a couple of ways:

  1. Use the -w option to change the working directory:

    -w, --workdir="" Working directory inside the container

    https://docs.docker.com/engine/reference/commandline/run/#set-working-directory--w

  2. Pass the entire argument to /bin/bash:

    docker run image /bin/bash -c "cd /path/to/somewhere; python a.py"
    
famousgarkin
  • 13,687
  • 5
  • 58
  • 74
John Petrone
  • 26,943
  • 6
  • 63
  • 68
13

You can also pipe commands inside Docker container, bash -c "<command1> | <command2>" for example:

docker run img /bin/bash -c "ls -1 | wc -l"

But, without invoking the shell in the remote the output will be redirected to the local terminal.

Alasdair
  • 298,606
  • 55
  • 578
  • 516
USER_1
  • 2,409
  • 1
  • 28
  • 28
11

bash -c works well if the commands you are running are relatively simple. However, if you're trying to run a long series of commands full of control characters, it can get complex.

I successfully got around this by piping my commands into the process from the outside, i.e.

cat script.sh | docker run -i <image> /bin/bash

user2966041
  • 527
  • 6
  • 9
  • You probably want to avoid the [useless use of `cat`](https://stackoverflow.com/questions/11710552/useless-use-of-cat) – tripleee Jan 10 '23 at 05:50
8

Just to make a proper answer from the @Eddy Hernandez's comment and which is very correct since Alpine comes with ash not bash.

The question now referes to Starting a shell in the Docker Alpine container which implies using sh or ash or /bin/sh or /bin/ash/.

Based on the OP's question:

docker run image sh -c "cd /path/to/somewhere && python a.py"

Nqsir
  • 829
  • 11
  • 19
6

TL;DR;

$ docker run --entrypoint /bin/sh image_name -c "command1 && command2 && command3"

A concern regarding the accepted answer is below.

Nobody has mentioned that docker run image_name /bin/bash -c just appends a command to the entrypoint. Some popular images are smart enough to process this correctly, but some are not.

Imagine the following Dockerfile:

FROM alpine
ENTRYPOINT ["echo"]

If you try building it as echo and running:

$ docker run echo /bin/sh -c date

You will get your command appended to the entrypoint, so that result would be echo "/bin/sh -c date".

Instead, you need to override the entrypoint:

$ docker run --entrypoint /bin/sh echo -c date

Docker run reference

igops
  • 475
  • 3
  • 7
4

If you want to store the result in one file outside the container, in your local machine, you can do something like this.

RES_FILE=$(readlink -f /tmp/result.txt)

docker run --rm -v ${RES_FILE}:/result.txt img bash -c "grep root /etc/passwd > /result.txt"

The result of your commands will be available in /tmp/result.txt in your local machine.

tripleee
  • 175,061
  • 34
  • 275
  • 318
Flamarion
  • 51
  • 3
2

For anyone else who came here looking to do the same with docker-compose you just need to prepend bash -c and enclose multiple commands in quotes, joined together with &&.

So in the OPs example docker-compose run image bash -c "cd /path/to/somewhere && python a.py"

S..
  • 5,511
  • 2
  • 36
  • 43
  • this one worked for me but the docker process exits after the command is executed – node_man Jan 29 '19 at 13:17
  • 1
    and how do I exit such a docker process? pressing ctrl C or ctrl Z or typing exit is not working or do I have to kill it by the docker stop command? – node_man Jan 30 '19 at 06:03
  • does `docker-compose down [service-name]` not work or? (and no worries you're welcome, happy to help :) ) – S.. Jan 30 '19 at 10:03
2

If you don't mind the commands running in a subshell, just put a set of outer parentheses around the multiple commands to run:

docker run image (cd /path/to/somewhere && python a.py)
Steve Chambers
  • 37,270
  • 24
  • 156
  • 208
1

In case it's not obvious, if a.py always needs to run in a particular directory, create a simple wrapper script which does the cd and then runs the script.

In your Dockerfile, replace

CMD [ 'python', 'a.py' ]

or whatever with

CMD [ '/wrapper' ]

and create a script wrapper in your root directory (or wherever it's convenient for you) with contents like

#!/bin/sh
set -e
cd /path/to/somewhere
python a.py

In many situations, perhaps also consider rewriting a.py so that it doesn't need a wrapper. Either make it os.chdir() where it needs to be, or have it look for its data files in a directory you configure in its environment or similar.

tripleee
  • 175,061
  • 34
  • 275
  • 318