How to use terminal cd command in .command files ON directory where file is in [duplicate]
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.
I'll claim there are three relevant concepts you are (/may be) running into trouble with here:
In a unix-style shell, when you use a command name that doesn't include a "/", it's looked for in the directories in the
PATH
environment variable (places like /bin, /usr/bin, etc), not anyplace relative to where you are (or some current script is). On the other hand, if it does contain a "/", it's treated as a path for the command/script to run. Using./
in front of a script name is just a way of specifying a path to a file in the current directory.-
In unix (including macOS), when you specify a relative path (to a document, script, or whatever), it's resolved relative to the process's current working directory; in a script, this is generally not the directory the script is in, but the directory the user (or whatever) was in when it ran the script.
So if you're in /Users/patlatus and you run a script in /Users/patlatus/Documents/scriptproject, and the script refers to
Subfolder/b.sh
, it'll look for /Users/patlatus/Subfolder/b.sh. Finding files relative to the script is tricky, and not always possible (or even well-defined), but bash you can usually derive it from$BASH_SOURCE
. Something like this:scriptdir=$(dirname "$BASH_SOURCE") # Find the current script's directory "$scriptdir/Subfolder/b.sh" # Run a script in a subdirectory of that
-
When you run a script just with its path (or name), it'll run as a subprocess. This means it inherits copies of any variables exported from the parent script, but not unexported variables, and any changes it makes to variables will not propagate back to the parent script's shell. This is different from how
call
works in a batch script;call
is closer to running another script with thesource
command (or its synonym.
), which runs it in the same shell process (with full access to variables etc). If you want this, use the.
orsource
command:scriptdir=$(dirname "$BASH_SOURCE") # Find the current script's directory . "$scriptdir/Subfolder/b.sh" # Source a script in a subdirectory of that
Note that since the sourced script shares variables, if it redefines
scriptdir
as its directory, that'll affect its value in the calling script as well. Note that the.
command has nothing whatsoever to do with the "." in./scriptname
-- that's just a relative path starting from the current directory, not a command.