Search code examples
unixpathmakefileenvironment-variablesshellcheck

make sees different PATH than account's .bashrc-configured PATH


I want to call ShellCheck, a Haskell program for linting shell scripts, from a Makefile.

When I install ShellCheck via cabal install, it is installed as ~/.cabal/bin/shellcheck. So, I have configured Bash accordingly:

$ cat ~/.bashrc
export PATH="$PATH:~/.cabal/bin"

$ source ~/.bashrc
$ shellcheck -V
ShellCheck - shell script analysis tool
version: 0.3.4
license: GNU Affero General Public License, version 3
website: http://www.shellcheck.net

This enables me to run shellcheck from any directory in Bash. However, when I try to call it from a Makefile, make cannot find shellcheck.

$ cat Makefile
shlint:
    -shlint lib/

shellcheck:
    -shellcheck lib/**

lint: shlint shellcheck

$ make shellcheck
shellcheck lib/**
/bin/sh: 1: shellcheck: not found
make: [shellcheck] Error 127 (ignored)

I think that make is not receiving the same PATH as my normal Bash shell. How can I fix this?


Solution

  • Try using $HOME, not ~:

    export PATH="$PATH:$HOME/.cabal/bin"
    

    The ~-means-home-directory feature is not supported in pathnames everywhere in all shells. When make runs a recipe it doesn't use the user's shell (that would be a disaster!) it uses /bin/sh always.

    On some systems (particularly Debian/Ubuntu-based GNU/Linux distributions) the default /bin/sh is not bash, but rather dash. Dash doesn't support ~ being expanded in the PATH variable.

    In general, you should reserve ~ for use on the command line as a shorthand. But in scripting, etc. you should always prefer to write out $HOME.

    ETA:

    Also, the double-star syntax lib/** is a non-standard feature of shells like bash and zsh and will not do anything special in make recipes. It is identical to writing lib/*.

    You can force make to use a different shell than /bin/sh by adding:

    SHELL := /bin/bash
    

    to your makefile, for example, but this makes it less portable (if that's an issue).