I’m writing a Bash script. I need the current working directory to always be the directory that the script is located in.
The default behavior is that the current working directory in the script is that of the shell from which I run it, but I do not want this behavior.
TL;DR
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
bashwith some name which tellsbashthe script to run. Thatbashthen callsdirnamewith the bash argument which points to the script. Finally, thatbashthen callscdwith the output ofdirnameas its argument.bashargumentdirnameargumentcdargumentfoo(found in$PATHat/path/to/foo)/path/to/foo/path/to/foo/path/tobash foofoofoo./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/foopa th/to/foopa th/to--help/foo--help/foo*--helpOn symlinks
The
cdcommand 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
* There is only one case where the argument to the
dirnamecould 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 runbash --help/foo, and bash does not know that option--help/fooand will therefore abort with an error message. The script will never execute, so it does not matter what thecd "$(dirname "$0")"would have executed.** Note that calling the script
--helpmakes the shell not find the command when you are typing--helpin the same directory. Alternatively, with$PATHcontaining the current directory, the script will be run as/path/to/--helpor./--help, always with something in front of the-character.Unless bash introduces command line arguments with a parameter separated by a
=, it is unlikely that you could pass a-argument to bash which contains a/later, and which is accepted by bash.If you can rely on
dirnameaccepting--argument (bash builtincdwill certainly accept--), you can change the script snippet toPlease 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.