skip to Main Content

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


  1. $_ 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.

    Login or Signup to reply.
  2. 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 to cd 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 particular RUN cd won’t have a persistent effect. I might suggest using the Dockerfile WORKDIR directive to change directories

    WORKDIR /sample_dir
    RUN touch sample_file
    

    which will also create the directory if it doesn’t exist.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search