2

I came across a script that is supposed to set up postgis in a docker container, but it references this "${psql[@]}" command in several places:

#!/bin/sh

# Perform all actions as $POSTGRES_USER
export PGUSER="$POSTGRES_USER"
# Create the 'template_postgis' template db
"${psql[@]}" <<- 'EOSQL'
CREATE DATABASE template_postgis;
UPDATE pg_database SET datistemplate = TRUE WHERE datname = 'template_postgis';
EOSQL

I'm guessing it's supposed to use the psql command, but the command is always empty so it gives an error. Replacing it with psql makes the script run as expected. Is my guess correct?

Edit: In case it's important, the command is being run in a container based on postgres:11-alpine.

GammaGames
  • 1,617
  • 1
  • 17
  • 32
  • 4
    There's supposed to be code before this that sets `psql` to an array containing the command and its arguments. – Barmar Aug 26 '19 at 21:30
  • @Barmar `psql` is a command from the installed program, but I don't see anywhere it is set in the script I linked at the top of the post. Do you think it's an error in the script? – GammaGames Aug 26 '19 at 21:32
  • 1
    Yeah, the script seems to be missing that. Maybe they expect you to add it, but they don't say so in the documentation. – Barmar Aug 26 '19 at 21:33
  • 1
    It could be that this script is intended to be sourced from another, but it's a bad design. – Dennis Williamson Aug 26 '19 at 21:33
  • Thank you for your help! The weird thing is that I was running this same script earlier today without issue, but this issue started happening a few hours ago. I don't see anything changed since my last commit, but I must've done anything. – GammaGames Aug 26 '19 at 21:35

4 Answers4

3

$psql is supposed to be an array containing the psql command and its arguments.

The script is apparently expected to be run from here, which does

psql=( psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --no-password )

and later sources the script in this loop:

        for f in /docker-entrypoint-initdb.d/*; do
            case "$f" in
                *.sh)
                    # https://github.com/docker-library/postgres/issues/450#issuecomment-393167936
                    # https://github.com/docker-library/postgres/pull/452
                    if [ -x "$f" ]; then
                        echo "$0: running $f"
                        "$f"
                    else
                        echo "$0: sourcing $f"
                        . "$f"
                    fi
                    ;;
                *.sql)    echo "$0: running $f"; "${psql[@]}" -f "$f"; echo ;;
                *.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${psql[@]}"; echo ;;
                *)        echo "$0: ignoring $f" ;;
            esac
            echo
        done

See Setting an argument with bash for the reason to use an array rather than a string.

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • It looks like it is run automatically by this script: https://github.com/docker-library/postgres/blob/master/11/docker-entrypoint.sh So your last point is correct – GammaGames Aug 26 '19 at 21:43
  • Where do you see `initdb-postgis.sh` in that script? – Barmar Aug 26 '19 at 21:45
  • 1
    Never mind, I discovered that it's looping through a directory, and this script should be in it. – Barmar Aug 26 '19 at 21:48
2

The #!/bin/sh and the [@] are incongruous. This is a bash-ism, where the psql variable is an array. This literal quote dollarsign psql bracket at bracket quote is expanded into "psql" "array" "values" "each" "listed" "and" "quoted" "separately." It's the safer way, e.g., to accumulate arguments to a command where any of them might have spaces in them.

psql=(/foo/psql arg arg arg) is the best way to define the array you need there.

Chad Miller
  • 1,435
  • 8
  • 11
1

It might look obscure, but it would work like so...

Let's say we have a bash array wc, which contains a command wc, and an argument -w, and we feed that a here document with some words:

wc=(wc -w)
"${wc[@]}" <<- words
      one
      two three
      four
words

Since there are four words in the here document, the output is:

4

In the quoted code, there needs to be some prior point, (perhaps a calling script), that does something like:

psql=(psql -option1 -option2 arg1 arg2 ... )

As to why the programmer chose to invoke a command with an array, rather than just invoke the command, I can only guess... Maybe it's a crude sort of operator overloading to compensate for different *nix distros, (i.e. BSD vs. Linux), where the local variants of some necessary command might have different names from the same option, or even use different commands. So one might check for BSD or Linux or a given version, and reset psql accordingly.

agc
  • 7,973
  • 2
  • 29
  • 50
  • Thank you for the explanation of what the code was actually doing, too! I assumed it was some useful but difficult to read bash-fu :) – GammaGames Aug 26 '19 at 21:47
1

The answer from @Barmar is correct.

The script was intended to be "sourced" and not "executed".

I faced the same problem and came to the same answer after I read that it had been reported here and fixed by "chmod". https://github.com/postgis/docker-postgis/issues/119

Therefore, the fix is to change the permissions. This can be done either in your git repository:

chmod -x initdb-postgis.sh

or add a line to your docker file.

RUN chmod -x /docker-entrypoint-initdb.d/10_postgis.sh

I like to do both so that it is clear to others. Note: if you are using git on windows then permission can be lost. Therefore, "chmod" in the docker file is needed.

Kristianw
  • 79
  • 3