skip to Main Content

I am creating a Dockerfile that needs to source a script before a shell is run.

ENTRYPOINT ["/bin/bash", "-rcfile","<(echo '. ./mydir/scripttosource.sh')"]

However, the script isn’t sourced as expected.

Combining these parameters on a command line (normal Linux instance, outside of any Docker container), it works properly, for example:

$ /bin/bash -rcfile <(echo '. ./mydir/scripttosource.sh')

So I took a look at what was actually used by the container when it was run.

$ docker ps --format "table {{.ID}} t {{.Names}} t {{.Command}}" --no-trunc
CONTAINER ID                                                         NAMES          COMMAND
70a5f846787075bd9bd55432dc17366268c33c1ab06fb36b23a50f5c3aef19bb     happy_cray     "/bin/bash -rcfile '<(echo '. ./mydir/scripttosource.sh')'"

Besides the fact that it properly identified the emotional state of Cray computers, Docker seems to be sneaking in undesired single quotes into the third parameter to ENTRYPOINT.

'<(echo '. ./mydir/scripttosource.sh')'

Thus the command actually being executed is:

$ /bin/bash -rcfile '<(echo '. ./mydir/scripttosource.sh')'

Which doesn’t work…

Now I realize there are more ways to skin this cat, and I could make this work a different way, I am curious about the insertion of single quotes to the third argument to ENTRYPOINT. Is there a way to avoid this?

Thank you,

2

Answers


  1. insertion of single quotes can be avoided by using escape characters in the third argument to ENTRYPOINT.

    ENTRYPOINT ["/bin/bash", "-rcfile","$(echo '. ./mydir/scripttosource.sh')"]
    
    Login or Signup to reply.
  2. At a super low level, the Unix execve(2) function launches a process by taking a sequence of words, where the first word is the actual command to run and the remaining words are its arguments. When you run a command interactively, the shell breaks it into words, usually at spaces, and then calls an exec-type function to run it. The shell also does other processing like replacing $VARIABLE references or the bash-specific <(subprocess) construct; all of these are at layers above simply "run a process".

    The Dockerfile ENTRYPOINT (and also CMD, and less frequently RUN) has two forms. You’re using the JSON-array exec form. If you do this, you’re telling Docker that you want to run the main container command with exactly these three literal strings as arguments. In particular the <(...) string is passed as a literal argument to bash --rcfile, and nothing actually executes it.

    The obvious answer here is to use the string-syntax shell form instead

    ENTRYPOINT /bin/bash -rcfile <(echo '. ./mydir/scripttosource.sh')
    

    Docker wraps this in an invocation of sh -c (or the Dockerfile SHELL). That causes a shell to preprocess the command string, break it into words, and execute it. Assuming the SHELL is bash and not a pure POSIX shell, this will handle the substitution.

    However, there are some downsides to this, most notably that the sh -c invocation "eats" all of the arguments that might be passed in the CMD. If you want your main container process to be anything other than an interactive shell, this won’t work.

    This brings you to the point of trying to find simpler alternatives to doing this. One specific observation is that the substitution here isn’t doing anything; <(echo something) will always produce the fixed string something and you can do it without the substitution. If you can avoid the substitution then you don’t need the shell either:

    ENTRYPOINT ["/bin/bash", "--rcfile", "./mydir/scripttosource.sh"]
    

    Another sensible approach here is to use an entrypoint wrapper script. This uses the ENTRYPOINT to run a shell script that does whatever initialization is needed, then exec "$@" to run the main container command. In particular, if you use the shell . command to set environment variables (equivalent to the bash-specific source) those will "stick" for the main container process.

    #!/bin/sh
    # entrypoint.sh
    
    # read the file that sets variables
    . ./mydir/scripttosource.sh
    
    # run the main container command
    exec "$@"
    
    # Dockerfile
    COPY entrypoint.sh ./           # may be part of some other COPY
    ENTRYPOINT ["./entrypoint.sh"]  # must be JSON-array syntax
    CMD ???
    

    This should have the same net effect. If you get a debugging shell with docker run --rm -it your-image bash, it will run under the entrypoint wrapper and see the environment variables. You can do other setup in the wrapper script if required. This particular setup also doesn’t use any bash-specific options, and might run better under minimal Alpine-based images.

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