Why is my $PATH different in the executed script?

echo $PATH inside gnome terminal:

/home/pc/less.js/bin:/home/pc/local/bin:/home/pc/local/bin:/home/pc/.rvm/gems/ruby-1.9.2-head/bin:/home/pc/.rvm/gems/ruby-1.9.2-head@global/bin:/home/pc/.rvm/rubies/ruby-1.9.2-head/bin:/home/pc/.rvm/bin:/usr/local/bin:/home/pc/local/bin:/usr/lib64/mpi/gcc/openmpi/bin:/home/pc/bin:/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/X11R6/bin:/usr/games:/usr/lib64/jvm/jre/bin:/home/pc/Programming/Software/tup:/home/pc/Programming/Libraries/depottools:/home/pc/Programming/Libraries/apache-maven-3.0.4/bin

From inside this script:

#!/bin/zsh
echo $PATH
while inotifywait -e modify /home/pc/vbox-shared/less; do
    lessc custom.less > /home/pc/vbox-shared/less/custom.css
done

/usr/lib64/mpi/gcc/openmpi/bin:/home/pc/bin:/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/X11R6/bin:/usr/games:/usr/lib64/jvm/jre/bin

As you can see, I modified my .zshrc file with this:

export PATH=/home/pc/less.js/bin:$PATH

Why does it not work in the script when executed as a file? The problem is that the lessc command is not being found.


Solution 1:

The script is run using /bin/zsh, which is not an interactive or login shell and doesn't load this file. From man zsh, emphasis mine:

Commands are first read from /etc/zshenv; this cannot be overridden. Subsequent behaviour is modified by the RCS and GLOBAL_RCS options; the former affects all startup files, while the second only affects global startup files (those shown here with an path starting with a /). If one of the options is unset at any point, any subsequent startup file(s) of the corresponding type will not be read. It is also possible for a file in $ZDOTDIR to re-enable GLOBAL_RCS. Both RCS and GLOBAL_RCS are set by default.

Commands are then read from $ZDOTDIR/.zshenv. If the shell is a login shell, commands are read from /etc/zprofile and then $ZDOTDIR/.zprofile. Then, if the shell is interactive, commands are read from /etc/zshrc and then $ZDOTDIR/.zshrc. Finally, if the shell is a login shell, /etc/zlogin and $ZDOTDIR/.zlogin are read.

The script inherits the environment from where it's called, and if this isn't another (interactive) shell, it won't contain the preferences you set in .zshrc.

You can set the PATH where it applies globally (e.g. /etc/zshenv), set it explicitly in the script directly, or change the shebang script header to run /bin/zsh -i instead, making it load .zshrc (quoting man zsh: Force shell to be interactive. It is still possible to specify a script to execute.).

Alternatively, just specify the full path to the program that isn't on the default PATH, e.g. /home/pc/less.js/bin/lessc.

Solution 2:

zsh startup files (a.k.a. rc files)

A filename below that is not a full path is implicitly preceded by “$ZDOTDIR/”, which is typically your home directory.

The order in which zsh sources startup script files is as follows.

/etc/zshenv – First, commands are read from here; options cannot be override this.

.zshenv

/etc/zprofile – login shell

.zprofile – login shell

/etc/zshrc – interactive shell

.zshrc – interactive shell

/etc/zlogin – login shell

.zlogin – login shell

Two zsh options affect whether zsh sources certain of the above files. (“RCS” is the plural of “rc”, rendered in upper case.)

RCS and GLOBAL_RCS zsh options are set by default.

  • RCS – affects all startup files
  • GLOBAL_RCS – affects only global startup files (pathnames starting with “/”)

If a startup file unsets one of these options, zsh skips subsequent startup files of the that type.

If a startup file sets the GLOBAL_RCS option, zsh sources subsequent global startup files.