Home > Blockchain >  Bash how to add conditional quoted argument '--pull "always"' to docker command
Bash how to add conditional quoted argument '--pull "always"' to docker command

Time:08-09

I am trying to conditionally add arguments to a docker call in a bash script but docker says the flag is unknown, but I can run the command verbatim by hand and it works.

I have tried a few strategies to add the command, including using a string instead of an array, and I have tried using a substitution like the solution here ( using ${array[@]/#/'--pull '} ): https://stackoverflow.com/a/68675860/10542275

 docker run --name application --pull "always" -p 3000:3000 -d private.docker.repository/group/application:version

This bash script

run() {
  getDockerImageName "/group" "$PROJECT_NAME:$VERSION" "latest";
  local imageName=${imageName};
  local additionalRunParameters=${additionalRunParameters};

  cd "$BASE_PATH/$PROJECT_NAME" || exit 1;

  stopAnyRunning "$PROJECT_NAME";

  echo docker run --name "$PROJECT_NAME" \
    "${additionalRunParameters[@]}" \
    -p 3000:3000 \
    -d "$imageName";

  // docker run --name application --pull "always" -p 3000:3000 -d private.docker.repository/group/application:version

  docker run --name "$PROJECT_NAME" \
    "${additionalRunParameters[@]}" \
    -p 3000:3000 \
    -d "$imageName";

  //unknown flag: --pull "always"
}

The helper 'getDockerImageName'

# Gets the name of the docker image to use for deploy.
# $1 - The path to the image in the container registry
# $2 - The name of the image and the tag
# $3 - 'latest' if the deploy should use the container built by CI
export imageName="";
export additionalRunParameters=();
getDockerImageName() {
  imageName="group/$2";
  if [[ $3 == "latest" ]]; then
    echo "Using docker image from CI...";
    docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "https://$DOCKER_BASE_URL";
    imageName="${DOCKER_BASE_URL}${1}/$2";
    additionalRunParameters=('--pull "always"');
  fi
}

CodePudding user response:

Don't put code (such as arguments) in a variable. Basically, use an array is good, and you are almost doing that. This line -

local additionalRunParameters=${additionalRunParameters};

is probably what's causing you trouble, along with

additionalRunParameters=('--pull "always"');

which is embedding the spaces between what you seem to have meant to be two operands (the option and its argument), turning them into a single string that is an unrecognized garble. //unknown flag: --pull "always" is trelling you the flag it's parsing is --pull "always", which is NOT the --pull flag is DOES know, followed by an argument.

Also,

export additionalRunParameters=(); # nope

arrays don't really export. Take that out, it will only confuse someone.

A much simplified set of examples:

$: declare -f a b c
a ()
{
    foo=('--pull "always"') # single value array
}
b ()
{
    echo "1: \${foo}='${foo}' <= scalar, returns first element of the array";
    echo "2: \"\${foo[@]}\"='${foo[@]}' <= returns entire array (be sure to put in quotes)";
    echo "3: \"\${foo[1]}\"='${foo[1]}' <= indexed, returns only second element of array"
}
c ()
{
    foo=(--pull "always") # two values in this array
}

$: a # sets ${foo[0]} to '--pull "always"'

$: b
1: ${foo}='--pull "always"' <= scalar, returns first element of the array
2: "${foo[@]}"='--pull "always"' <= returns entire array (be sure to put in quotes)
3: "${foo[1]}"='' <= indexed, returns only second element of array

$: c # setd ${foo[0]} to '--pull' and ${foo[1]} to "always"

$: b
1: ${foo}='--pull' <= scalar, returns first element of the array
2: "${foo[@]}"='--pull always' <= returns entire array (be sure to put in quotes)
3: "${foo[1]}"='always' <= indexed, returns only second element of array

So what you need is: getDockerImageName(){ . . . # stuff additionalRunParameters=( --pull "always" ); # no single quotes }

and just take OUT

local additionalRunParameters=${additionalRunParameters}; # it's global, no need

You have one more issue though - "${additionalRunParameters[@]}" \ is good, as long as the array isn't empty. In your example it will apparently always be loaded with the same values, so I don't see why you are adding all this extra complication of putting it into a global array that gets loaded incidentally in another function... seems like an antipattern. Just put the arguments you are universally enforcing anyway on the command itself.

However, on the possibility that you simplified some details out, then if this array is ever empty it's going to pass a literal quoted empty string as an argument on the command line, and you're likely to get something like the following error:

$: docker run --name foo "" -p 3000:3000 -d bar # docker doesn't understand the empty string
docker: invalid reference format.

Maybe, rather than

additionalRunParameters=( --pull "always" ); # no single quotes

and

"${additionalRunParameters[@]}" \

what you really want is

pull_always=true

and

${pull_always:  --pull always } \

...with no quotes, so that if the var has been set (with anything) it evaluates to the desired result, but if it's unset and unquoted it evaluates to nothing and gets ignored, as nothing actually gets passed in - not even a null string.

Good luck. Let us know if you need more help.

  • Related