261

In docker I want to do this:

git clone XYZ
cd XYZ
make XYZ

However because there is no cd command, I have to pass in the full path everytime (make XYZ /fullpath). Any good solutions for this?

jww
  • 83,594
  • 69
  • 338
  • 732
RParadox
  • 4,827
  • 4
  • 19
  • 28

4 Answers4

625

To change into another directory use WORKDIR. All the RUN, CMD and ENTRYPOINT commands after WORKDIR will be executed from that directory.

RUN git clone XYZ 
WORKDIR "/XYZ"
RUN make
Javier Castellanos
  • 7,358
  • 2
  • 11
  • 18
  • 48
    Using WORKDIR is also cited as a [dockerfile best practice](https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/#workdir) – Martin Woolstenhulme Jun 01 '16 at 20:00
  • 15
    I believe this is a more appropriate answer to the question – Juan Leni Oct 04 '17 at 06:19
  • 1
    Note that this creates multiple layers (I think?) – Sebi Oct 12 '19 at 10:55
  • 3
    This should be the accepted answer. Tested and can confirm it works – user1258361 May 27 '20 at 20:40
  • I think that this answer is rather not enough. It did not help me in my case, it only helps in standard cases where you use for example `apt-get` or other commands that work in the `sh` shell (= Dockerfile default). If you need the `bash` instead of the `sh` shell, as it is normal for many bash commands that you also need in a Dockerfile, you need to call the `bash` shell before using the command. See [below answer](https://stackoverflow.com/a/66733880/11154841). The WORKDIR will not work in that isolated `bash`-`RUN`, the bash will start with the /root, if you do not change the ".bashrc". – questionto42 Mar 21 '21 at 15:11
200

You can run a script, or a more complex parameter to the RUN. Here is an example from a Dockerfile I've downloaded to look at previously:

RUN cd /opt && unzip treeio.zip && mv treeio-master treeio && \
    rm -f treeio.zip && cd treeio && pip install -r requirements.pip

Because of the use of '&&', it will only get to the final 'pip install' command if all the previous commands have succeeded.

In fact, since every RUN creates a new commit & (currently) an AUFS layer, if you have too many commands in the Dockerfile, you will use up the limits, so merging the RUNs (when the file is stable) can be a very useful thing to do.

Alister Bulman
  • 31,832
  • 9
  • 65
  • 105
  • 159
    In case you're wondering, the effect of the `cd` only lasts for the current `RUN` command. The next `RUN` will start from the current `WORKDIR`. – Ritchie Oct 17 '17 at 06:56
  • This doesn't work for me. I get an error that the directory doesn't exist. Only `WORKDIR` works. – mbomb007 Aug 17 '20 at 21:14
  • 1
    This should not be the solution. The question is simply how to get CD to work. – Jeremiah Adams Sep 23 '20 at 15:04
3

I was wondering if two times WORKDIR will work or not, but it worked :)

FROM ubuntu:18.04

RUN apt-get update && \
    apt-get install -y python3.6

WORKDIR /usr/src

COPY ./ ./

WORKDIR /usr/src/src

CMD ["python3", "app.py"]
Mian Asbat Ahmad
  • 2,594
  • 7
  • 36
  • 60
0

Mind that if you must run in bash shell, you need not just the RUN make, but you need to call the bash shell before, since in Docker, you are in the sh shell by default.

Taken from /bin/sh: 1: gvm: not found, which would say more or less:

Your shell is /bin/sh, but source expects /bin/bash, perhaps because it puts its initialization in ~/.bashrc.

In other words, this problem can occur in any setting where the "sh" shell is used instead of the "bash", causing "/bin/sh: 1: MY_COMMAND: not found".

In the Dockerfile case, use the recommended

RUN /bin/bash -c 'source /opt/ros/melodic/setup.bash'

or with the "[]" (which I would rather not use):

RUN ["/bin/bash", "-c", "source /opt/ros/melodic/setup.bash"]

Every new RUN of a bash is isolated, "starting at 0". For example, mind that setting WORKDIR /MY_PROJECT before the bash commands in the Dockerfile does not affect the bash commands since the starting folder would have to be set in the ".bashrc" again. It needs cd /MY_PROJECT even if you have set WORKDIR.

Side-note: do not forget the first "/" before "opt/../...". Else, it will throw the error:

/bin/bash: opt/ros/melodic/setup.bash: No such file or directory

Works:

 => [stage-2 18/21] RUN ["/bin/bash", "-c", "source /opt/ros/melodic/setup.bash"]                                  0.5s
 => [stage-2 19/21] [...]

See “/bin/sh: 1: MY_COMMAND: not found” at SuperUser for some more details on how this looks with many lines, or how you would fill the ".bashrc" instead. But that goes a bit beyond the actual question here.

PS: You might also put the commands you want to execute in a single bash script and run that bash script in the Dockerfile (though I would rather put the bash commands in the Dockerfile as well, just my opinion):

#!/bin/bash
set -e

source /opt/ros/melodic/setup.bash
questionto42
  • 1,420
  • 1
  • 10
  • 30