How can I satisfy shellcheck without causing script failure?

1.8k Views Asked by At

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.

4

There are 4 best solutions below

0
On BEST ANSWER

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"

1
On

Looks like the man page https://www.mankier.com/1/shellcheck describes ways to override errors.

Ignore certain errors:

shellcheck --exclude SC2086 file.sh

If you can configure shellcheck arguments to run it that way it should fix it. Normally I don’t like overriding linters, but in this case, looking at what that variable does, I don’t see a way around it. Can’t double quote a bunch of args to docker. They really need to be passed as separate args.

0
On

Just decided to disable the shellcheck using

# shellcheck disable=SC2086
docker run --rm \
    "${INTERACTIVE_MODE_FLAG}" \
    ${EXTRA_DOCKER_ARGS} \
    "${IMAGE}":"${IMAGE_VERSION}" "$@"

UPDATE. I decided the answer from @that-other-guy was better so I changed to that.

2
On

Without disabling shellcheck warnings:

#!/usr/bin/env bash

# Word split EXTRA_DOCKER_ARGS into extra_docker_args_array
read -r -a extra_docker_args_array <<<"$EXTRA_DOCKER_ARGS"

docker run --rm \
    "$INTERACTIVE_MODE_FLAG" \
    "${extra_docker_args_array[@]}" \
    "$IMAGE:$IMAGE_VERSION" "$@"