1

For the following code, I'm getting SC2089 "Quotes/backslashes will be treated literally, use an array"

# Quotes/backslashes will be treated literally, use an array
CMD_AVAIL_MSG='%s is required for this script but the "%s" command is missing. Exiting...'
if [ -z "$(which valet)" ]; then
    printf -v err $CMD_AVAIL_MSG "Laravel Valet" "valet";
    echo $err; # there's other stuff I do here, but using echo for conciseness
    exit 1;
fi

However, converting this to an array gives me shellcheck warning SC2059 "Don't use variables in the printf format string, use '...%s...' $foo"

CMD_AVAIL_MSG=("%s" 'is required for this script but the "' '%s' '" command is missing. Exiting...')
if [ -z "$(which valet)" ]; then
    # Don't use variables in the printf format string, use '...%s...' "$foo"
    printf -v err "${CMD_AVAIL_MSG[@]}" "Laravel Valet" "valet";
    echo $err; # there's other stuff I do here, but using echo for conciseness
    exit 1;
fi

How do I resolve both of these warnings?

mindfullsilence
  • 344
  • 9
  • 23
  • Each copy of printf only takes _one_ format string, and then a series of arguments after. `CMD_AVAIL_MSG` being an array really doesn't make sense. – Charles Duffy Apr 27 '23 at 16:12
  • What does the actual output you want when this message is printed look like? – Charles Duffy Apr 27 '23 at 16:14
  • Most of the errors go away if you quote here: `echo "$err"`. – Barmar Apr 27 '23 at 16:18
  • Ignore the warning about using a variable in the format string, if your goal is to make it dynamic. – Barmar Apr 27 '23 at 16:20
  • 1
    The intention of [SC2059](https://www.shellcheck.net/wiki/SC2059) is to discourage the use of variable expansions within format strings (`"...$var..."` instead of `"...%s..." "$var"`). Since you are simply putting a complete and good format string in a variable, the warning is a false positive in this case. Either ignore it or suppress it. This is covered in the "Exceptions" section of the [SC2059](https://www.shellcheck.net/wiki/SC2059) web page. – pjh Apr 27 '23 at 16:28

1 Answers1

2

Warning SC2059 is intended to prevent people from passing data in the format-string position. You aren't doing that (your "data" is in fact a format string), so it isn't intended for you and can be safely disabled.

On the other hand, you are still misusing printf: Because each invocation of printf accepts only a single format string, CMD_AVAIL_MSG should be a regular string, not an array. (It also should be lowercase; all-caps names are reserved by POSIX-specified convention for variables that reflect or modify behavior of the shell or other POSIX-specified tools).

cmd_avail_msg='"%s" is required for this script but the "%s" command is missing. Exiting...'
if ! command -v valet >/dev/null 2>&1; then
    # shellcheck disable=SC2059
    printf -v err "$cmd_avail_msg" 'Laravel Valet' valet
    echo "$err" >&2
    exit 1;
fi

Note the use of if ! command -v valet >/dev/null 2>&1 instead of if [ -z "$(which valet)" ]. This is both more efficient and more portable: which is a separate executable being run as a subprocess, whereas command -v is a POSIX-specified command internal to the shell itself.


The above being said, this looks like a place where you can, and perhaps should, use a function instead of a variable with a format string -- that way you can reuse the entire code block, not just the single message.

check_for_cmd() {
  local cmd=$1 name=$2 err
  command -v "$cmd" >/dev/null 2>&1 && return 0
  printf -v err \
    '"%s" is required for this script but the "%s" command is missing. Exiting...' \
    "$name" "$cmd"
  # do other stuff here
  echo "$err" >&2
  exit 1
}

check_for_cmd valet "Laravel Valet"
check_for_cmd other "Something Else"
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441