bashscriptingshellcheck

Why does shellcheck fail when a source file is representing the variables at the top of a script?


Ubuntu 16.04
Bash 4.3.48

Why does shellcheck fail when a source file is representing the $variables at the top of a script?

Here is a simple script:

#!/bin/bash

. .sourcefile

echo "Today is ${day}."

Here is my source file:

day="Monday"

Here is the reply from shellcheck:

me@myserver:~$ shellcheck start.sh

In start.sh line 5:
echo "Today is ${day}."
               ^-- SC2154: day is referenced but not assigned.

Is there a way to let shellcheck know the $variables are in the source file?


Here is what I did to make this work on Ubuntu 16.04

@Dash-o explained the procedure below:

First, add the source directive on the line above the source file declaration like so:
# shellcheck source=/path/to/sourcefile

#!/bin/bash

# shellcheck source=./.sourcefile
. .sourcefile

echo "Today is ${day}."

Next, execute shellcheck with the -x option before the script like so:
shellcheck -x start.sh

me@myserver:~$ shellcheck -x start.sh

When using the -x option, shellcheck will follow the source file declared directly below the source directive. When I executed the command, I received the following error:

me@myserver:~$ shellcheck -x start.sh
unrecognized option `-x'

Usage: shellcheck [OPTIONS...] FILES...
  -e CODE1,CODE2..  --exclude=CODE1,CODE2..  exclude types of warnings
  -f FORMAT         --format=FORMAT          output format
  -s SHELLNAME      --shell=SHELLNAME        Specify dialect (bash,sh,ksh)
  -V                --version                Print version information

@Bayou mentioned my OS needed a newer version of shellcheck. I checked my Ubuntu 16.04 installation. My server had shellcheck 0.3.7, which is the latest version Ubuntu 16.04 had to offer, so I grabbed the latest binary from the shellcheck developers and installed it.

me@myserver:~$ mkdir .work && cd .work
me@myserver:~/.work$ wget -q https://github.com/koalaman/shellcheck/releases/download/stable/shellcheck-stable.linux.x86_64.tar.xz
me@myserver:~/.work$ ls

shellcheck-stable.linux.x86_64.tar.xz

me@myserver:~/.work$ tar xvf shellcheck-stable.linux.x86_64.tar.xz

shellcheck-stable/README.txt
shellcheck-stable/LICENSE.txt
shellcheck-stable/shellcheck

me@myserver:~/.work$ sudo chown root: shellcheck-stable/shellcheck
me@myserver:~/.work$ sudo mv /usr/bin/shellcheck .
me@myserver:~/.work$ sudo mv shellcheck-stable/shellcheck /usr/bin/
me@myserver:~/.work$ cd ../
me@myserver:~$ rm -rf .work/
me@myserver:~$ shellcheck -V

ShellCheck - shell script analysis tool
version: 0.7.1
license: GNU General Public License, version 3
website: https://www.shellcheck.net

I ran the command shellcheck -x start.sh and I received zero errors.

me@myserver:~$ shellcheck -x start.sh

I then forced shellcheck to give me a few errors. I added cat $myVariable to the end of my script.

me@myserver:~$ echo "cat \$myVariable" >> start.sh  
me@myserver:~$ cat start.sh
#!/bin/bash

# shellcheck source=./.sourcefile
. .sourcefile

echo "Today is ${day}."
cat $myVariable

I tested my theory and shellcheck followed my sourcefile and gave me errors I was expecting.

me@myserver:~$ shellcheck -x start.sh

In start.sh line 7:
cat $myVariable
    ^---------^ SC2154: myVariable is referenced but not assigned.
    ^---------^ SC2086: Double quote to prevent globbing and word splitting.

Did you mean:
cat "$myVariable"

For more information:
  https://www.shellcheck.net/wiki/SC2154 -- myVariable is referenced but not ...
  https://www.shellcheck.net/wiki/SC2086 -- Double quote to prevent globbing ...

Then I executed shellcheck without the -x option and to my surpise I received the following errors:

me@myserver:~$ shellcheck start.sh

In start.sh line 4:
. .sourcefile
  ^---------^ SC1091: Not following: ./.sourcefile was not specified as input (see shellcheck -x).


In start.sh line 6:
echo "Today is ${day}."
               ^----^ SC2154: day is referenced but not assigned.


In start.sh line 7:
cat $myVariable
    ^---------^ SC2154: myVariable is referenced but not assigned.
    ^---------^ SC2086: Double quote to prevent globbing and word splitting.

Did you mean:
cat "$myVariable"

For more information:
  https://www.shellcheck.net/wiki/SC2154 -- day is referenced but not assigned.
  https://www.shellcheck.net/wiki/SC1091 -- Not following: ./.sourcefile was ...
  https://www.shellcheck.net/wiki/SC2086 -- Double quote to prevent globbing ...

So shellcheck updated to the latest version and without the -x option, will also give you an error about you mising the -x option on the command line. So I decided to comment out the sourcefile directive in my start.sh

me@myserver:~$ sed -i '3s/./#\ &/' start.sh
me@myserver:~$ cat start.sh
#!/bin/bash

# # shellcheck source=./.sourcefile
. .sourcefile

echo "Today is ${day}."
cat $myVariable

Now to see what shellcheck thinks

me@myserver:~$ shellcheck -x start.sh

In start.sh line 7:
cat $myVariable
    ^---------^ SC2154: myVariable is referenced but not assigned.
    ^---------^ SC2086: Double quote to prevent globbing and word splitting.

Did you mean:
cat "$myVariable"

For more information:
  https://www.shellcheck.net/wiki/SC2154 -- myVariable is referenced but not ...
  https://www.shellcheck.net/wiki/SC2086 -- Double quote to prevent globbing ...

Whaaaaa? So it seems the sourcefile directive is really not needed with my simple script or is shellcheck still seeing the sourcefile directive being commented out with the number sign as the number sign already starts the sourcefile directive? So I removed the sourcefile directive altogether and commented out the last line which references the myVariable variable that is not assigned in the sourcefile.

me@myserver:~$ sed -i '3d' start.sh
me@myserver:~$ sed -i '$d' start.sh
me@myserver:~$ cat start.sh
#!/bin/bash

. .sourcefile

echo "Today is ${day}."  

Now to see what shellcheck reports:

me@myserver:~$ shellcheck -x start.sh

No errors with the -x option. Now to check if the sourcefile directive is not needed at the top of a simple shell script with latest version of shellcheck on Ubuntu 16.04, I executed shellcheck without the -x option.

me@myserver:~$ shellcheck start.sh

In start.sh line 3:
. .sourcefile
  ^---------^ SC1091: Not following: .sourcefile was not specified as input (see shellcheck -x).


In start.sh line 5:
echo "Today is ${day}."
               ^----^ SC2154: day is referenced but not assigned.

For more information:
  https://www.shellcheck.net/wiki/SC2154 -- day is referenced but not assigned.
  https://www.shellcheck.net/wiki/SC1091 -- Not following: .sourcefile was no...

So, in short, if shellcheck is not following your source file, update shellcheck from the developer's github - (https://github.com/koalaman/shellcheck) and inform shellcheck there is a sourcefile to follow by using the -x option like so:

shellcheck -x script.sh  

I hope this helps someone as this site helps me each and everyday!


Solution

  • Shellcheck is a static analysis tool. It does not handle dynamic paths (based on variables, or expressions). As an alternative, consider adding the source directive.

    Example:

    # shellcheck source=./lib.sh
    source "$(find_install_dir)/lib.sh"
    

    The source= directive tells shellcheck the location of the dynamically generated filename. From the question, this should be the (most likely, relative) location of the .sourcefile.

    Documented in https://github.com/koalaman/shellcheck/blob/master/shellcheck.1.md