I'm a docker newbie and I'm learning how to write Dockerfile. I've been experimenting with different ways of writing RUN instructions, and I've come across some behavior that I don't understand why, so I'd like to know about it.
When I built and run the following Dockerfile.v1, the sample_file exists in the sample_dir. This is as expected.
FROM ubuntu:latest
RUN mkdir sample_dir && cd sample_dir && touch sample_file
Then, I decided to use shell variables for general purpose writing, I created the following Dockerfile.v2 and RUN.
FROM ubuntu:latest
RUN mkdir sample_dir && cd $_ && touch sample_file
Then, sample_file was created in /root, which is not what I expected. Since the sample_dir itself was created in /, I assume WORKING DIRECTORY is /.
Why was it created in the root user's home directory? How was $_ interpreted?
❯ docker run -it --rm 1ce bash 2023/09/17 18:01:16
root@2ce4010e4613:/#
root@2ce4010e4613:/# ll
total 60
drwxr-xr-x 1 root root 4096 Sep 17 09:01 ./
drwxr-xr-x 1 root root 4096 Sep 17 09:01 ../
-rwxr-xr-x 1 root root 0 Sep 17 09:01 .dockerenv*
lrwxrwxrwx 1 root root 7 Aug 16 02:25 bin -> usr/bin/
drwxr-xr-x 2 root root 4096 Apr 18 2022 boot/
drwxr-xr-x 5 root root 360 Sep 17 09:01 dev/
drwxr-xr-x 1 root root 4096 Sep 17 09:01 etc/
drwxr-xr-x 2 root root 4096 Apr 18 2022 home/
lrwxrwxrwx 1 root root 7 Aug 16 02:25 lib -> usr/lib/
drwxr-xr-x 2 root root 4096 Aug 16 02:25 media/
drwxr-xr-x 2 root root 4096 Aug 16 02:25 mnt/
drwxr-xr-x 2 root root 4096 Aug 16 02:25 opt/
dr-xr-xr-x 212 root root 0 Sep 17 09:01 proc/
drwx------ 1 root root 4096 Sep 17 07:39 root/
drwxr-xr-x 5 root root 4096 Aug 16 04:54 run/
drwxr-xr-x 2 root root 4096 Sep 17 07:39 sample_dir/
lrwxrwxrwx 1 root root 8 Aug 16 02:25 sbin -> usr/sbin/
drwxr-xr-x 2 root root 4096 Aug 16 02:25 srv/
dr-xr-xr-x 13 root root 0 Sep 17 09:01 sys/
drwxrwxrwt 2 root root 4096 Aug 16 04:52 tmp/
drwxr-xr-x 11 root root 4096 Aug 16 02:25 usr/
drwxr-xr-x 11 root root 4096 Aug 16 04:52 var/
root@2ce4010e4613:/# ll /root/
total 16
drwx------ 1 root root 4096 Sep 17 07:39 ./
drwxr-xr-x 1 root root 4096 Sep 17 09:01 ../
-rw-r--r-- 1 root root 3106 Oct 15 2021 .bashrc
-rw-r--r-- 1 root root 161 Jul 9 2019 .profile
-rw-r--r-- 1 root root 0 Sep 17 07:39 sample_file
root@2ce4010e4613:/#
I know I can avoid this behavior by not using $_ or by using WORKDIR instruction, but I just want to know why this is happening.
I thought it might be because the default is /bin/sh, so I specified bash in SHELL INSTRUCTION as shown below, but there was no change.
https://docs.docker.com/engine/reference/builder/#shell
FROM ubuntu:latest
RUN mkdir sample_dir && cd $_ && touch sample_file
SHELL [ "/bin/bash" ]
My environment
❯ docker --version 2023/09/19 21:38:37
Docker version 24.0.5, build ced0996
❯ sw_vers 2023/09/19 21:42:27
ProductName: macOS
ProductVersion: 13.5.2
BuildVersion: 22G91
detailed info
❯ docker build -f ./Dockerfile_old . 2023/10/03 18:13:48
[+] Building 0.2s (6/6) FINISHED docker:desktop-linux
=> [internal] load build definition from Dockerfile_old 0.0s
=> => transferring dockerfile: 140B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/ubuntu:latest 0.0s
=> [1/2] FROM docker.io/library/ubuntu:latest 0.0s
=> [2/2] RUN mkdir sample_dir && cd $_ && touch sample_file 0.1s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:3f2dc4ad66693b6306ee812371b79e6a2984a18e6590509be9a98108a235f7ff 0.0s
❯ docker history --no-trunc 3f2 2023/10/03 18:15:25
IMAGE CREATED CREATED BY SIZE COMMENT
sha256:3f2dc4ad66693b6306ee812371b79e6a2984a18e6590509be9a98108a235f7ff 37 seconds ago RUN /bin/sh -c mkdir sample_dir && cd $_ && touch sample_file # buildkit 0B buildkit.dockerfile.v0
<missing> 7 days ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 7 days ago /bin/sh -c #(nop) ADD file:8540670760767f19eaf101fbce1da1881a2f24a7d65da6abdedc644b8fb00463 in / 69.2MB
<missing> 7 days ago /bin/sh -c #(nop) LABEL org.opencontainers.image.version=22.04 0B
<missing> 7 days ago /bin/sh -c #(nop) LABEL org.opencontainers.image.ref.name=ubuntu 0B
<missing> 7 days ago /bin/sh -c #(nop) ARG LAUNCHPAD_BUILD_ARCH 0B
<missing> 7 days ago /bin/sh -c #(nop) ARG RELEASE 0B
❯ docker images | grep ubuntu 2023/10/03 18:16:23
ubuntu latest bf9e5b7213b4 7 days ago 69.2MB
$_
only really makes sense in interactive Bash. A more standard and portable solution is to use a regular variable.
RUN d=sample_dir && mkdir "$d" && cd "$d" && touch sample_file
... though of course, the cd
is pretty much useless here.
RUN d=sample_dir && mkdir "$d" && touch "$d"/sample_file
Anyway, your attempt to set SHELL
is wrong. You want
SHELL [ "/bin/bash", "-c" ]
Without the -c
, your RUN
statements would be interpreted as names of shell script files to try to execute.
Of course, when $_
is empty, cd $_
expands to just cd
which changes to the current user's home directory.