TL;DR
#!/bin/bash
cd "$(dirname "$0")"
The long winded explanation
How does this work and how does it deal with edge and corner cases?
- You type a command launching the script in your interactive shell.
- That interactive shell calls
bash
with some name which tells bash
the script to run.
- That
bash
then calls dirname
with the bash argument which points to the script.
- Finally, that
bash
then calls cd
with the output of dirname
as its argument.
script invocation command |
bash argument |
dirname argument |
cd argument |
foo (found in $PATH at /path/to/foo ) |
/path/to/foo |
/path/to/foo |
/path/to |
bash foo |
foo |
foo |
. |
/foo |
/foo |
/foo |
/ |
./foo |
./foo |
./foo |
. |
"/pa th/to/foo" |
/pa th/to/foo |
/pa th/to/foo |
/pa th/to |
"./pa th/to/foo" |
./pa th/to/foo |
./pa th/to/foo |
./pa th/to |
"../pa th/to/foo" |
../pa th/to/foo |
../pa th/to/foo |
../pa th/to |
"../../pa th/to/foo" |
../../pa th/to/foo |
../../pa th/to/foo |
../../pa th/to |
"pa th/to/foo" |
pa th/to/foo |
pa th/to/foo |
pa th/to |
--help/foo |
--help/foo * |
N/A |
N/A |
--help |
N/A ** |
N/A |
N/A |
On symlinks
The cd
command will follow symlinks if they are involved. A symlink usually exists to be followed, so in most situations following the symlink is the correct thing to do. Why would it be a problem for the code in the script to follow a symlink when it was just fine to follow that same symlink a few microseconds ago when loading the script?
On command arguments starting with hyphens
Elaborating on the two cases of arguments starting with hyphens in above table (marked with * and **, respectively):
* There is only one case where the argument to the dirname
could begin with a -
, and that is the relative path case --help/foo
. If the script is in a subdirectory called --help
, the script execution will run bash --help/foo
, and bash does not know that option --help/foo
and will therefore abort with an error message. The script will never execute, so it does not matter what the cd "$(dirname "$0")"
would have executed.
** Note that calling the script --help
makes the shell not find the command when you are typing --help
in the same directory. Alternatively, with $PATH
containing the current directory, the script will be run as /path/to/--help
or ./--help
, always with something in front of the -
character.
Unless bash introduces command line arguments with a parameter separated by a =
, it is unlikely to impossible to pass a -
argument to bash which contains a /
later, and which is accepted by bash.
If you can rely on dirname
accepting --
argument (bash builtin cd
will certainly accept --
), you can change the script snippet to
cd -- "$(dirname -- "$0")"
Please do comment if you can figure out a way to construct an argument beginning with -
which can be sneaked past bash.
Nota bene
The above snippet also works with non-bash /bin/sh
.
Best Answer
Since
$0
holds the full path of the script that is running, you can usedirname
against it to get the path of the script:so if you for example store it in
/tmp/a.sh
then you will see an output like:so
Using
dirname "$0"
will allow you to keep track of the original path.Again, since you have the path in
$0
you cancd
back to it.