How to write folder-agnostic shell script on Mac as I can do in Windows

bashcommand linescript

I would like to write a set of shell scripts which would execute smoothly on Mac disregarding where they are called from.

I am currently struggling about how to implement this.

On windows, I could write three batch scripts

A.bat in folder A
B.bat in folder Subfolder, which is subfolder of A and which path is A/Subfolder
C.bat in folder Subfolder, which is subfolder of A and which path is A/Subfolder

In A.bat I could write

call Subfolder/B.bat

in B.Bat I could write

call C.bat

and both scripts A and B would execute successfully.

On Mac, however, I have to prepend ./ to every script so that it is recognized as script.

So when I write in a.sh code:

Subfolder/b.sh

and then in b.sh code

./c.sh

when I execute ./b.sh from terminal it is executed successfully.

However, when I execute ./a.sh from a parent folder in terminal, it fails with error

./a.sh
Subfolder/b.sh: line 1: ./c.sh: No such file or directory

Is this possible on Mac to write a code in b.sh which would execute disregarding if it is called from the current folder or from the parent folder?

Best Answer

There are two concepts relevant to understand what's going on here:

  • when running binaries or shell scripts as name-of-executable, the shell looks into $PATH for the list of directories the binary/shell script could be stored it. The first match found is then used to run the binary/script, if no match is found you get an error message. If instead you run it as ./name-of-executable or /path/to/executable $PATH is not searched but the path is taken either relative to the current directory (if it starts with ./ or ../) or absolute
  • each process (including each shell script) has a default directory it runs in and which gets inherited to any child processes started

So in your case if you run ./a.sh the current directory remains the one a.sh is stored in even when subfolder/b.sh is running, which then lets ./c.sh fail.

The easy way out of this to always change directories before calling child processes (and of course changing back afterwards). So in a.sh you would write

cd subfolder
./b.sh
cd ..

which would allow b.sh to call ./c.sh within the same subfolder without problems.