How do I get the path of the directory in which a Bash script is located, inside that script?
For instance, let's say I want to use a Bash script as a launcher for another application. I want to change the working directory to the one where the Bash script is located, so I can operate on the files in that directory, like so:
$ ./application
I tried every one of these and none of them worked. One was very close but had a tiny bug that broke it badly; they forgot to wrap the path in quotation marks.
Also a lot of people assume you're running the script from a shell so forget when you open a new script it defaults to your home.
Try this directory on for size:
/var/No one/Thought/About Spaces Being/In a Directory/Name/And Here's your file.text
This gets it right regardless how or where you run it.
So to make it actually useful here's how to change to the directory of the running script:
Hope that helps
I don't think this is as easy as others have made it out to be. pwd doesn't work, as the current dir is not necessarily the directory with the script. $0 doesn't always have the info either. Consider the following three ways to invoke a script.
In the first and third ways $0 doesn't have the full path info. In the second and third, pwd do not work. The only way to get the dir in the third way would be to run through the path and find the file with the correct match. Basically the code would have to redo what the OS does.
One way to do what you are asking would be to just hardcode the data in the /usr/share dir, and reference it by full path. Data shoudn't be in the /usr/bin dir anyway, so this is probably the thing to do.
Here is the simple, correct way:
Explanation:
${BASH_SOURCE[0]}
- the full path to the script. The value of this will be correct even when the script is being sourced, e.g.source <(echo 'echo $0')
prints bash, while replacing it with${BASH_SOURCE[0]}
will print the full path of the script. (Of course, this assumes you're OK taking a dependency on Bash.)readlink -f
- Recursively resolves any symlinks in the specified path. This is a GNU extension, and not available on (for example) BSD systems. If you're running a Mac, you can use Homebrew to install GNUcoreutils
and supplant this withgreadlink -f
.And of course
dirname
gets the parent directory of the path.pwd
can be used to find the current working directory, anddirname
to find the directory of a particular file (command that was run, is$0
, sodirname $0
should give you the directory of the current script).However,
dirname
gives precisely the directory portion of the filename, which more likely than not is going to be relative to the current working directory. If your script needs to change directory for some reason, then the output fromdirname
becomes meaningless.I suggest the following:
This way, you get an absolute, rather then relative directory.
Since the script will be run in a separate bash instance, there is no need to restore the working directory afterwards, but if you do want to change back in your script for some reason, you can easily assign the value of
pwd
to a variable before you change directory, for future use.Although just
solves the specific scenario in the question, I find having the absolute path to more more useful generally.
is a useful one-liner which will give you the full directory name of the script no matter where it is being called from.
It will work as long as the last component of the path used to find the script is not a symlink (directory links are OK). If you also want to resolve any links to the script itself, you need a multi-line solution:
This last one will work with any combination of aliases,
source
,bash -c
, symlinks, etc.Beware: if you
cd
to a different directory before running this snippet, the result may be incorrect!Also, watch out for
$CDPATH
gotchas, and stderr output side effects if the user has smartly overridden cd to redirect output to stderr instead (including escape sequences, such as when callingupdate_terminal_cwd >&2
on Mac). Adding>/dev/null 2>&1
at the end of yourcd
command will take care of both possibilities.To understand how it works, try running this more verbose form:
And it will print something like: