96

I'm trying to understand what this Docker entrypoint does.

It seems to me that's a very common pattern when writing Dockerfiles, but my bash skills are limited and I have no idea of all the special bash symbols kung fu.

Also, it's hard to google for "--", "$!" etc. What are these called in bash world?

To summarize, what is the line bellow trying to do?

if [ "${1#-}" != "$1" ]; then
  set -- haproxy "$@"
fi
Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
Lucas Pottersky
  • 1,063
  • 1
  • 8
  • 6
  • 1
    Also, to provide the simple answer to "what is the line below trying to do"... this allows you to pass arguments to the main process the container is going to run. It checks "does 'haproxy' not equal 'haproxy --help' for exmaple and if so, grabs those arguments and passes them along. For instance, `docker run haproxy --help` will pass my "--help" to haproxy inside the container. – Evan Morrison Oct 07 '20 at 20:35

2 Answers2

127

The set command (when not setting options) sets the positional parameters eg

$ set a b c
$ echo $1
a
$ echo $2
b
$ echo $3
c

The -- is the standard "don't treat anything following this as an option"

The "$@" are all the existing position paramters.

So the sequence

set -- haproxy "$@"

Will put the word haproxy in front of $1 $2 etc.

eg

$ echo $1,$2,$3
a,b,c
$ set -- haproxy "$@"
$ echo $1,$2,$3,$4   
haproxy,a,b,c
Stephen Harris
  • 42,369
  • 5
  • 94
  • 123
  • 1
    i don't see any difference using "--" or not though. Tried here and got the same results. I'd love if you could improve the answer to highlight the difference. – Lucas Pottersky Sep 09 '16 at 15:22
  • 17
    Compare `set -- -z 2 3 4` and `set -z 2 3 4`. The `--` stops the `-a` being interpreted as an option. In this case it's not _necessary_, but it's "good practice" to get into the habit of using `--` when you're note sure what is following :-) – Stephen Harris Sep 09 '16 at 16:09
  • 3
    The other main difference is that if you are sourcing a script inside of another script, using `set -- $arg1 $arg2` will allow your sourced script to read the arguments, otherwise only Bash supports passing arguments directly to a sourced script (and that can be tricky to notice). – dragon788 Aug 14 '18 at 18:34
  • @StephenHarris your comment made me understand complete.! kudos – chaosguru Apr 02 '20 at 08:36
  • 5
    @StephenHarris, I think it should be "The `--` stops the `-z` being interpreted as an options", right? – LRDPRDX Aug 15 '20 at 06:44
  • I cannot contrive a shell script that demonstrates the purpose of this. When you say "The `--` stops the `-a` being interpreted as an option"... what would an example script look like where that happens? I see the `-a` option of `set` causes the "...export attrbute to be set for each variable to which assrgnment is performed..." I create two scripts, one with and one without `set -- "$@"`, and pass `-a` as an argument, and both behave the exact same way as far as I can tell. Can anyone point out my misunderstanding? Thanks! – Eric Ihli Sep 19 '20 at 15:44
  • 1
    @EricIhli Simple script; `#!/bin/bash set $1 $2 $3`. Run that with `-c` and you'll get an error similar to `./x: line 2: set: -c: invalid option` which shows the command is being interpreted as options (do `help set` in bash and see what `set -a` does). But if the line said `set -- $1 $2 $3` then you don't get an error and it sets the positional paramters. – Stephen Harris Sep 19 '20 at 22:28
  • I think Stephen's answer meant to say "stops the *-z*" but otherwise great, I get it now. – RichieHH Dec 04 '20 at 02:05
1

The set command supports options (including flags) and arguments

set [-abefhkmnptuvxBCEHPT] [-o option-name] [--] [-] [argument …]
  • options can be used to change how the command works (i.e. how it interprets filenames)
  • arguments tell commands what to do: i.e. your user-input

e.g. the set command has an option

-a: Each variable or function that is created or modified is given the export attribute and marked for export to the environment of subsequent commands.

When we execute:

$ set -a 12; echo $1
12

you can see that $1 is set to 12, but -a was consumed by the set command

If we really want to set -a as $1 (and 12 as $2), we can use -- to tell the set command to stop processing options (i.e. everything that follows is an argument)

$ set -- -a 12; echo $1 $2
-a 12
TmTron
  • 111
  • 3