138

In a bash script I'm writing, I use source to include the variable defined in a configuration file. The script to be executed is act.sh, while the script to be sourced is act.conf.sh, so in act.sh I have:

source act.conf.sh

However this only works when running act.sh in the directory containing it, since act.conf.sh there refers to the file placed under the working directory. Is there a solution to make it refer to the file relative to the executing script without invoking cd? Thanks.

Ryan Li
  • 9,020
  • 7
  • 33
  • 62

4 Answers4

153

See: BASH FAQ entry #28: "How do I determine the location of my script? I want to read some config files from the same place."

Any solution isn't going to work 100% of the time:

It is important to realize that in the general case, this problem has no solution. Any approach you might have heard of, and any approach that will be detailed below, has flaws and will only work in specific cases. First and foremost, try to avoid the problem entirely by not depending on the location of your script!

If you need to write a very reusable tool, then taking the correct path as a parameter to your script is going to be the most reliable method.

Assuming your script is only going to be run from certain shells, and only with a little bit of flexibility required, you can probably relax some of this paranoia. It is still good to look at your options. There are common patterns that people use that are particularly problematic.

In particular, the FAQ recommends avoiding the very commonly used $0 variable:

Nothing that reads $0 will ever be bulletproof, because $0 itself is unreliable.

As an alternative, you could use $BASH_SOURCE instead. Something like this:

source "${BASH_SOURCE%/*}/act.conf.sh"

There are some caveats to this solution, too. Check out the FAQ page to see the trade-offs between different solutions. They seem to recommend cd in combination with $BASH_SOURCE in cases where it will work for you, as you get a handy error condition when it fails to expand properly.

Merlyn Morgan-Graham
  • 58,163
  • 16
  • 128
  • 183
Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
  • 1
    Thanks, I would use `readlink`. Wasn't able to determine the keywords to search for so posted the question here.... – Ryan Li Jul 12 '11 at 05:07
  • 10
    Can you pull in the relevant info into the answer in case the bash faq happens to be down? – Merlyn Morgan-Graham Nov 23 '14 at 23:09
  • 3
    Link recommends: `cd "${BASH_SOURCE%/*}" || exit`, or `read somevar < "${BASH_SOURCE%/*}/etc/somefile"`, Link *Strongly Discourages* `$0` "Nothing that reads $0 will ever be bulletproof, because $0 itself is unreliable." – ThorSummoner Sep 29 '15 at 23:47
  • 1
    Expanded on your answer to avoid the "just a link" problem. Feel free to keep editing it or revert it as you wish. I make no guarantees that my additions accurately map to the original FAQ's intent :) – Merlyn Morgan-Graham Apr 24 '16 at 21:54
  • 11
    What does the %/* after BASH_SOURCE do and how? – Silicomancer Dec 15 '16 at 12:54
  • 1
    https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html – Ignacio Vazquez-Abrams Dec 15 '16 at 14:26
  • "If you need to write a very reusable tool, then **taking the correct path as a parameter to your script is going to be the most reliable method**." - hmm ... that kind of defeats the purpose when the very benefit of the script is that its users do *not* have to remember and input the relative locations of the files the script invokes. – O. R. Mapper Feb 09 '17 at 00:15
  • `. "${BASH_SOURCE%/*}/relative.sh"` seems to work on bash 4 though you might need to do additional steps to pass arguments properly – Dragas Feb 04 '19 at 08:35
  • Taking required path as a parameter requires the _user_ of my script to type more and learn more and know more and understand more. Sometimes you must deal with "plain users" who just want to type the name of the executable and have everything else taken care of ("press Enter, dad!"). We're talking users who will run away screaming before you finish pronouncing the four-syllable word "parameter". – Szczepan Hołyszewski Feb 13 '22 at 22:00
43

See this: Bash: How _best_ to include other scripts?

I suggest to use:

source $(dirname $0)/act.conf.sh
Community
  • 1
  • 1
vagovszkym
  • 1,432
  • 12
  • 16
  • 11
    This is not the answer you seek. `$0` is inherently buggy. See the top voted and accepted answer for why, or simply read http://mywiki.wooledge.org/BashFAQ/028 - short workaround for the question asked is something like `source "${BASH_SOURCE%/*}/act.conf.sh"` – Merlyn Morgan-Graham Apr 24 '16 at 21:43
17

Add this on top of the script. Then, all the subsequent code will be get executed relative to the location of the script.

cd "$(dirname "${BASH_SOURCE[0]}")"
Gayan Weerakutti
  • 11,904
  • 2
  • 71
  • 68
6

Try the following:

source ${BASH_SOURCE[0]/%act.sh/act.conf.sh}
Clueless
  • 3,984
  • 1
  • 20
  • 27