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" ]
2
Answers
$_
only really makes sense in interactive Bash. A more standard and portable solution is to use a regular variable.… though of course, the
cd
is pretty much useless here.Anyway, your attempt to set
SHELL
is wrong. You wantWithout the
-c
, yourRUN
statements would be interpreted as names of shell script files to try to execute.Of course, when
$_
is empty,cd $_
expands to justcd
which changes to the current user’s home directory.The POSIX shell specification only defines a limited set of Special Parameters,
$@
,$*
,$#
,$?
,$-
,$$
,$!
, and$0
. The default shell is a POSIX-compliant shell and not GNU bash (see for example this question) so other shell-specific extensions aren’t available.$_
isn’t one of the standard-defined special parameters, so it expands to an empty string. Since you haven’t quoted the variable expansion either, the argument tocd
just gets lost, and that’s how you wind up in/root
.In a Docker context more specifically, also remember that each
RUN
line starts a new shell in a new container, and parts of the shell context that change are lost. In particularRUN cd
won’t have a persistent effect. I might suggest using the DockerfileWORKDIR
directive to change directorieswhich will also create the directory if it doesn’t exist.