bashshellshellcheck

How to pass a glob to a shell script via an environment variable while passing ShellCheck


Let's say I have a shell script, test.sh, that is supposed to echo some number of values. I'd like to be able to pass those values in via an environment variable with glob expansion:

$ ls
1.js  2.js  test.sh*

$ FOO="*.js" bash ./test.sh
1.js 2.js

Easy, you say! Just write

#!/bin/bash
echo $FOO

and indeed, this works. But it does not pass ShellCheck, which calls us out for using implicit globbing/expansion (SC2086). (And this is fair; in the original code, the echo line was cp $FOO $BAR, where indeed we didn't want to expand $BAR; this error catches bugs.)

So, in a quest to make this more explicit, I was hoping that maybe the following would work:

#!/bin/bash
array=( $FOO )
echo "${array[@]}"

but no: we end up with SC2206.

Is there a canonical way of doing this kind of thing, which falls within what the ShellCheck authors consider best practices? (Feel free to use the online checker at www.shellcheck.net to test it out.) Is this the wrong approach entirely? It seems unlikely the ShellCheck authors never thought of this use case...


Solution

  • There doesn't appear to be a safe workaround for ShellCheck's warnings in this case. However, ShellCheck's diagnostics is not universal truth. As you may have noticed, documentation of some of its warnings includes an Exceptions section. For SC2206 it reads:

    Exceptions:

    If you have already taken care (through setting IFS and set -f) to have word splitting work the way you intend, you can ignore this warning.

    Now, ShellCheck provides directives to ignore warnings on a case-by-case basis:

    Shellcheck directives allow you to selectively ignore warnings, and takes the form of comments in files:

    hexToAscii() {
      # shellcheck disable=SC2059
      printf "\x$1"
    }
    

    Supported directives are

    • disable to disable warnings:

      # shellcheck disable=code[,code...]
      statement_where_warning_should_be_disabled
      

      ...

    Directives instead of or immediately after the shebang apply to the entire script. Otherwise, they are scoped to the structure that follows it (such as all branches of a case statement, or an entire function).

    So you can suppress your warning as follows:

    #!/usr/bin/env bash
    
    # The following line is needed so that the shellcheck directive
    # below doesn't have global effect
    :
    
    # shellcheck disable=SC2206
    array=( $FOO )
    echo "${array[@]}"