linuxbashshellcheck

How can I satisfy shellcheck without causing script failure?


I am currently testing out GitHub Actions and the quickstart explains how to add super-linter to a repo which is an easy way to apply linting across a whole repo - I like this idea so I have added it to my repo, one of the linters it applies is shellcheck and it has thrown up some errors on one of my shell scripts.

That shell script issues docker run using the following code:

docker run --rm \
    "${INTERACTIVE_MODE_FLAG}" \
    ${EXTRA_DOCKER_ARGS} \
    "${IMAGE}":"${IMAGE_VERSION}" "$@"

Shellcheck is throwing SC2086 Double quote to prevent globbing and word splitting on ${EXTRA_DOCKER_ARGS}. I can easily remove the Shellcheck error by changing the code to:

docker run --rm \
    "${INTERACTIVE_MODE_FLAG}" \
    "${EXTRA_DOCKER_ARGS}" \
    "${IMAGE}":"${IMAGE_VERSION}" "$@"

(note wrapping ${EXTRA_DOCKER_ARGS} in quotes)

docker: invalid reference format

If I add set -x to the top of the script it shows what the problem is:

+ docker run --rm -it '' myimage:mytag
docker: invalid reference format.

Notice how two extra apostrophes have been added to the command.

My question is simply... how can I solve this? I want to eradicate the Shellcheck error but still have a functioning script.


Solution

  • Your code is using a string as a list, and this conceptual mismatch is causing the warning. It is a real problem and you should address it.

    You should either:

    A. Make EXTRA_DOCKER_ARGS a list to begin with.

    OR

    B. Keep EXTRA_DOCKER_ARGS as a string, and put some thought into how you want it split into arguments (on whitespace? line by line? shell quoted arguments?). This needs to match the expectations of whoever sets the variable.


    For A, you can simplify specify the variable as an array and expand it accordingly:

    EXTRA_DOCKER_ARGS=()
    docker run --rm \
        "${INTERACTIVE_MODE_FLAG}" \
        "${EXTRA_DOCKER_ARGS[@]}" \
        "${IMAGE}":"${IMAGE_VERSION}" "$@"
    

    For B, you can e.g. treat it as a shell quoted string which requires expansion by eval:

    extras=()
    eval "extras+=( $EXTRA_DOCKER_ARGS )"
    docker run --rm \
        "${INTERACTIVE_MODE_FLAG}" \
        "${extras[@]}" \
        "${IMAGE}":"${IMAGE_VERSION}" "$@"
    

    If you do not fix this issue and continue to rely on implicit word splitting, then you will be unable to pass extra arguments containing spaces, such as -v "$HOME/My Documents:/mnt"