74

I have a utility consisting of a couple of directories with some bash scripts and supporting files that will be deployed to several machines, possibly in a different directory on each machine. The scripts need to be able to reference paths relative to themselves, so I need to be able to get the path to the file that's currently being executed.

I am aware of the dirname $0 idiom which works perfectly when my script is called directly. Unfortunately there is a desire to be able to create symlinks to these scripts from a totally different directory and still have the relative pathing logic work.

An example of the overall directory structure is as follows:

/
 |-usr/local/lib
 |  |-foo
 |  |  |-bin
 |  |  |  |-script.sh
 |  |  |-res
 |  |  |  |-resource_file.txt
 |-home/mike/bin
 |  |-link_to_script (symlink to /usr/local/lib/foo/bin/script.sh)

How can I reliably reference /usr/local/lib/foo/res/resource_file.txt from script.sh whether it is invoked by /usr/local/lib/foo/bin/script.sh or ~mike/bin/link_to_script?

5 Answers 5

102

Try this as a general purpose solution:

DIR="$(cd "$(dirname "$0")" && pwd)"

In the specific case of following symlinks, you could also do this:

DIR="$(dirname "$(readlink -f "$0")")"
6
  • Works perfectly and is way simpler than the stuff I was trying. Thanks!
    – Mike Deck
    Jul 27, 2011 at 20:19
  • @MikeDeck Enjoy. Note that I fixed some quoting issues since first posting ... you might use an option from the latest edit in the event that you have spaces or other wacky characters in your path sometime. Even if it doesn't matter for this project, you might copy the code from your own scripts down the road and having the best option matters then!
    – Caleb
    Jul 27, 2011 at 20:40
  • 2
    I should clarify, the second one works perfectly. The first solution you gave does not work for symlinks on my system.
    – Mike Deck
    Jul 27, 2011 at 20:47
  • 4
    @MikeDeck: The first solution works for the typical case of having various directories symlinked as part of the path, but won't help if the final binary is a symlink. That's what the second one manages to take care of.
    – Caleb
    Jul 27, 2011 at 21:19
  • 1
    Doesn't work if the script file is sourced (e.g $ source ./sript.sh) This works for both cases: DIR="$(cd "$(dirname "$BASH_SOURCE")" && pwd)" Sep 6, 2018 at 18:18
9

one line:

cd $(dirname $([ -L $0 ] && readlink -f $0 || echo $0))
2
  • 2
    One line answers are typically not the most helpful. Please consider expanding your answer to include explanation, links or documentation which further support your suggested solution.
    – HalosGhost
    Aug 29, 2014 at 2:57
  • 1
    actually, you don't need to do this, readlink version of the original answer works just fine if is no symlink involved at all Feb 16, 2016 at 14:16
4

readlink -f doesn't work for me, I've this mistake:

readlink: illegal option -- f
usage: readlink [-n] [file ...]

But this works fine:

DIR="$(dirname "$(readlink "$0")")"
echo $DIR
2
  • 3
    Then you are on a Mac and this should answer your question.
    – user232326
    Oct 8, 2018 at 6:04
  • Yes, I'm on MacOs @isaac, but I don't have any question. I posted the command which works on my side. Oct 11, 2018 at 11:51
0

One small addition to the above script. The -P option to pwd follows symlinks

DIR="$(cd "$(dirname "$0")" && pwd -P)"
1
  • 7
    This only work when the symlink is part of the directory structure. It does not work when the file itself is linked. Dec 22, 2015 at 11:23
0

In my case (on macOS) I had to use:

DIR=$(cd "$(dirname "$(readlink "$0" || echo "$0")")" && pwd)

This is because the symlink or the script behind can both be called by users. So in both cases the original files directory has to be resolved. It works as following:

  • readlink "$0" tries to get the file behind the symlink
  • if the script is no symlink echo "$0" prints the physical location of the script
  • in both cases we have the scrips location which we get the directory name of using dirname
  • then we cd into that directory and print the absolute path using pwd

If you aren't on macOS just add the -f parameter for readlink.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.