25

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!

Vituvo
  • 1,008
  • 1
  • 9
  • 29
  • 1
    The `"$(pwd)/"` part is not necessary. You can just use `. .sourcefile`. – Socowi Oct 28 '19 at 09:32
  • 1
    @CuriousSam : I don't think that shell check is actually reading the sourced files. In the general case, Shellcheck can't know what file you are sourcing (imagine that you would source something like `. /$(myprog $RANDOM)`. In particular in your case, shellcheck can not reasonibly derive the value of `pwd`. – user1934428 Oct 28 '19 at 10:23
  • I corrected the ```$(pwd)```. – Vituvo Oct 28 '19 at 11:02
  • 2
    Most of this should be an `Answer` instead. – miyalys May 29 '20 at 11:05

4 Answers4

26

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

quapka
  • 2,799
  • 4
  • 21
  • 35
dash-o
  • 13,723
  • 1
  • 10
  • 37
  • 1
    I read the documentation and tried it ```shellcheck source-path=./.sourcefile start.sh``` and it printed ```shellcheck: source-path=./.sourcefile: openFile: does not exist (No such file or directory)```. I also tired ```shellcheck source=./.sourcefile start.sh``` and it still failed. I must have the wrong syntax? – Vituvo Oct 28 '19 at 11:00
  • You have to insert a COMMENT into your script, just before the 'source' statement. This is NOT a command line parameter. Please take a look at the documentation link – dash-o Oct 28 '19 at 11:17
  • Right on. All works perfectly now. I appreciate the help. – Vituvo Oct 28 '19 at 16:48
  • I updated shellcheck again from the devs github and it seems as if the _sourcefile directive_ is not needed anymore, but the **-x** option is still needed to inform ```shellcheck``` to look for a souce file declared with the word ```source``` or a period ```.``` at the beginning of a line. – Vituvo Jun 18 '20 at 09:53
  • Worth also noting that one could disable this check with `-e SC1090` opton. – Hi-Angel Mar 12 '21 at 11:26
  • This didn't work for me at first because the path to the sourced dir must be _relative to the dir you are in when you call `shellcheck -x path/to/script.sh`._ I figured that out then [wrote all about that here](https://stackoverflow.com/a/75341911/4561887). – Gabriel Staples Feb 03 '23 at 23:33
  • can `source=` start from the home path? – alper Feb 25 '23 at 11:34
4

I use shellcheck 0.4.4 and it also says:

In start.sh line 2: . "$(pwd)"/.sourcefile
^-- SC1090: Can't follow non-constant source. Use a directive to specify location.

That means you'll have to use absolute paths, but newer versions might support -P.

If you switched to a non-constant source, it will not work as well because .sourcefile needs a shebang and you have to add this file with -x as such:

shellcheck -x .sourcefile start.sh

It still will complain about:

day="Monday"
^-- SC2034: day appears unused. Verify it or export it.

Bayou
  • 3,293
  • 1
  • 9
  • 22
  • That was purposely done to trigger the shellcheck warnings. ```day="Monday"``` is in the source file. While using shellcheck and calling for a source file inside the script, you have to add ```# shellcheck source=./.sourcefile.sh``` at the top of the script. You then tell shellcheck to look for the source with the -x on the command line ```shellcheck -x start.sh``` – Vituvo Dec 26 '19 at 04:40
  • i _still_ get that even with `source "$HOME"/.bashrc` – Fuseteam May 26 '21 at 04:19
  • @Fuseteam Try ```# shellcheck source=~/.bashrc``` – Vituvo May 29 '21 at 05:21
  • 3
    same issue, in the end i just disabled it with `# shellcheck source=/dev/null` – Fuseteam May 29 '21 at 18:48
3

VSCode fix

In VSCode, you can set a parameter in settings:

{
  "shellcheck.customArgs": ["-x"]
}

Answer from: https://unix.stackexchange.com/a/534690/231320

Jerry Green
  • 1,244
  • 1
  • 15
  • 25
0

It's more nuanced than the main answer suggests.

How to properly fix the SC1091 shellcheck error and get shellcheck to follow and check your sourced scripts

Summary

Put all 3 of these things together:

  1. Ensure you use shellcheck -x path/to/script.sh.
  2. Ensure you use a # shellckeck source=path/to/sourced_script.sh directive comment.
  3. Ensure the path you use in the directive above is relative to the directory you are in when calling shellcheck -x path/to/script.sh.

Details

To remove the SC1091 (info): Not following: path/to/script.sh was not specified as input (see shellcheck -x). error, you need all 3 of these things:

  1. Add -x when calling shellcheck:

    # ok
    shellcheck -x path/to/script.sh
    
    # NOT ok
    shellcheck path/to/script.sh
    
  2. Add the proper shellcheck directive comment above the area of concern:

    Inside path/to/script.sh:

    # ok
    
    # shellcheck source=bash/sound_bell_lib.sh
    . "$SCRIPT_DIRECTORY/sound_bell_lib.sh"
    
    # NOT ok
    . "$SCRIPT_DIRECTORY/sound_bell_lib.sh"
    
  3. Ensure the path you use inside your shellcheck source= directive comment is relative to the path you are in when you call shellcheck!

    If:

    1. Your script is at ~/dev/script.sh.
    2. The script you are sourcing from ~/dev/script.sh is at ~/dev/script2.sh.
    3. You are in the ~ directory when running the shellcheck command.
    4. The shellcheck command you are running is shellcheck -x dev/script.sh

    Then, the shellcheck directive path you must use is dev/script2.sh, like this:

    # shellcheck source=dev/script2.sh
    . "$SCRIPT_DIRECTORY/script2.sh"
    

    But, if you are in the ~/dev dir when running the shellcheck command, and the shellcheck command you are running is shellcheck -x script.sh, then the shellcheck directive path you must use is ./script2.sh, or simply script2.sh, instead, like this:

    # shellcheck source=./script2.sh
    . "$SCRIPT_DIRECTORY/script2.sh"
    
Gabriel Staples
  • 36,492
  • 15
  • 194
  • 265