449

The way you would normally include a script is with "source"

eg:

main.sh:

#!/bin/bash

source incl.sh

echo "The main script"

incl.sh:

echo "The included script"

The output of executing "./main.sh" is:

The included script
The main script

... Now, if you attempt to execute that shell script from another location, it can't find the include unless it's in your path.

What's a good way to ensure that your script can find the include script, especially if for instance, the script needs to be portable?

codeforester
  • 39,467
  • 16
  • 112
  • 140
Aaron H.
  • 6,407
  • 5
  • 25
  • 30
  • 3
    see this one: http://stackoverflow.com/questions/59895/can-a-bash-script-tell-what-directory-its-stored-in – Lluís Feb 18 '14 at 12:44
  • 1
    Related / also answering this question, by the way: here is my answer on how to get the `SCRIPT_DIRECTORY` path of the script being run, so you can use it to import ([via `source` or `.`](https://stackoverflow.com/a/62626515/4561887)) other scripts relative to the current script: [How can I get the source directory of a Bash script from within the script itself?](https://stackoverflow.com/a/60157372/4561887). (My answer there is so far down the list, having this link will save you some scrolling there too :)). My answer there can be considered a variation of the accepted answer here. – Gabriel Staples Oct 03 '21 at 02:39

23 Answers23

267

I tend to make my scripts all be relative to one another. That way I can use dirname:

#!/bin/sh

my_dir="$(dirname "$0")"

"$my_dir/other_script.sh"
alexia
  • 14,440
  • 8
  • 42
  • 52
Chris Boran
  • 4,781
  • 2
  • 25
  • 25
  • 7
    This will not work if the script is executed through $PATH. then `which $0` will be useful – Hugo Sep 13 '09 at 13:49
  • 46
    There is no reliable way to determine the location of a shell script, see http://mywiki.wooledge.org/BashFAQ/028 – Philipp Sep 11 '10 at 18:35
  • 17
    @Philipp, The author of that entry is correct, it is complex, and there are gotchas. But it's missing some key points, first, the author assumes a whole lot of things about what you are going to be doing with your bash script. I wouldn't expect a python script to run without it's dependencies either. Bash is a glue language that allows you to do things quickly that would be hard otherwise. When you need your build system to work, pragmatism (And a nice warning about the script not being able to find dependencies) wins. – Aaron H. Oct 29 '10 at 16:54
  • This will not work when an included script needs to include another, since $0 is set to "-bash". Anybody knows a solution for this case? – haridsv Feb 10 '14 at 10:41
  • 13
    Just learned about `BASH_SOURCE` array, and how the first element in this array always points to the current source. – haridsv Feb 11 '14 at 09:31
  • 6
    This will not work if the scripts are different locations. e.g. /home/me/main.sh calls /home/me/test/inc.sh as dirname will return /home/me. sacii answer using BASH_SOURCE is a better solution http://stackoverflow.com/a/12694189/1000011 – opticyclic May 13 '14 at 19:09
  • No good. BASH_SOURCE is better: http://stackoverflow.com/questions/192292/bash-how-best-to-include-other-scripts/12694189#12694189 – Viacheslav Dobromyslov Oct 01 '14 at 07:47
  • Hint: use `source "$my_dir/other_script.sh"` (with bash) to execute the script in the current shell. – Matthias Nov 25 '19 at 08:02
  • BASH_SOURCE sounds great... for bash. Although somehow, I have a feeling it isn't very portable. If you really want the location of the script, use findutils with a unique name. – Nate T Oct 03 '21 at 01:13
  • $0 would give included script, whereas `source` way gives original. – Martian2020 Dec 10 '21 at 21:41
237

I know I am late to the party, but this should work no matter how you start the script and uses builtins exclusively:

DIR="${BASH_SOURCE%/*}"
if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi
. "$DIR/incl.sh"
. "$DIR/main.sh"

. (dot) command is an alias to source, $PWD is the Path for the Working Directory, BASH_SOURCE is an array variable whose members are the source filenames, ${string%substring} strips shortest match of $substring from back of $string

Gabriel Staples
  • 36,492
  • 15
  • 194
  • 265
sacii
  • 2,379
  • 1
  • 12
  • 3
  • 15
    This is the only answer in the thread that consistently worked for me – Justin May 09 '13 at 08:11
  • This is the answer! I would only make it a method, or an alias, rather than a variable (included scripts would rewrite the variable in a parent script if they used the same mechanism to include their subscripts). – Petr Skocik Jun 04 '15 at 21:14
  • 3
    @sacii May I know when is the line `if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi` needed? I can find a need for it if the commands are being pasted to a bash prompt to run. However, if running inside a script file context, I can't see a need for it... – Johnny Wong Mar 08 '16 at 09:04
  • 2
    It is also worth noting that it works as expected across multiple `source`s (i mean, if you `source` a script that `source`s another in another directory and so on, it still works). – Ciro Costa Apr 07 '16 at 00:57
  • 4
    Shouldn't this be `${BASH_SOURCE[0]}` as you only want the latest called? Also, using `DIR=$(dirname ${BASH_SOURCE[0]})` will allow you to get rid of the if-condition – kshenoy Jul 14 '16 at 15:23
  • I don't like this bash specific implementation which is not working with zsh – A B Aug 18 '17 at 01:20
  • 2
    This fails if you invoke the script from the script directory with `$ bash script.sh` where a leading dot is not included in `$BASH_SOURCE` and `DIR` resolves to `script.sh` whereas `$(dirname $BASH_SOURCE)` correctly resolves to `.` – user1823021 Mar 26 '19 at 05:00
  • Both `${BASH_SOURCE%/*}` and `$(dirname ${BASH_SOURCE[0]})` worked for me. This worked consistently both in files that were `source`d or executed via `./` in several different configurations (unlike the accepted answer). – Meiogordo Aug 14 '19 at 08:53
  • 1
    And I still thinks this is terrible. I wish there would be just one liner for this without worrying if this will work in nested imports, current or working dir, functions etc. – n.podbielski Sep 12 '19 at 13:04
  • 1
    Are you willing to make this snippet available under a no attribute required license, like [CC0](https://creativecommons.org/share-your-work/public-domain/cc0/) or just release it into the public domain? I'd like to use this verbatim but it's annoying to put the attribution at the top of every single script! – BeeOnRope Nov 17 '19 at 02:59
  • `DIR=$(dirname $BASH_SOURCE)` is best! – Christopher King Oct 16 '20 at 21:31
58

An alternative to:

scriptPath=$(dirname $0)

is:

scriptPath=${0%/*}

.. the advantage being not having the dependence on dirname, which is not a built-in command (and not always available in emulators)

tardate
  • 16,424
  • 15
  • 50
  • 50
52

If it is in the same directory you can use dirname $0:

#!/bin/bash

source $(dirname $0)/incl.sh

echo "The main script"
dsm
  • 10,263
  • 1
  • 38
  • 72
32

I think the best way to do this is to use the Chris Boran's way, BUT you should compute MY_DIR this way:

#!/bin/sh
MY_DIR=$(dirname $(readlink -f $0))
$MY_DIR/other_script.sh

To quote the man pages for readlink:

readlink - display value of a symbolic link

...

  -f, --canonicalize
        canonicalize  by following every symlink in every component of the given 
        name recursively; all but the last component must exist

I've never encountered a use case where MY_DIR is not correctly computed. If you access your script through a symlink in your $PATH it works.

Community
  • 1
  • 1
Mat131
  • 446
  • 5
  • 7
  • Nice and simple solution, and works famously for me in as many variations of script invocation that I could think of. Thanks. – Brian Cline Oct 11 '13 at 23:59
  • Aside from issues with missing quotes, is there any actual use case where you would want to resolve the symbolic links rather than use `$0` directly? – l0b0 May 31 '14 at 12:47
  • 1
    @l0b0: Imagine your script is `/home/you/script.sh` You can `cd /home` and run your script from there as `./you/script.sh` In this case `dirname $0` will return `./you` and including other script will fail – dr.scre Aug 24 '16 at 15:47
  • 1
    Great suggestion, however, I needed to do the following in order for it to read my variables in ` `MY_DIR=$(dirname $(readlink -f $0)); source $MY_DIR/incl.sh` – Frederick Ollinger Jan 25 '18 at 18:49
26

A combination of the answers to this question provides the most robust solution.

It worked for us in production-grade scripts with great support of dependencies and directory structure:

#!/bin/bash

# Full path of the current script
THIS=`readlink -f "${BASH_SOURCE[0]}" 2>/dev/null||echo $0`

# The directory where current script resides
DIR=`dirname "${THIS}"`

# 'Dot' means 'source', i.e. 'include':
. "$DIR/compile.sh"

The method supports all of these:

  • Spaces in path
  • Links (via readlink)
  • ${BASH_SOURCE[0]} is more robust than $0
Brian Cannard
  • 852
  • 9
  • 20
22
SRC=$(cd $(dirname "$0"); pwd)
source "${SRC}/incl.sh"
Max Arnold
  • 427
  • 1
  • 5
  • 11
  • 1
    I suspect that you got down voted for the "cd ..." when dirname "$0" should accomplish the same thing... – Aaron H. Nov 19 '10 at 16:38
  • 9
    This code will return absolute path even when script is executed from current directory. $(dirname "$0") alone will return just "." – Max Arnold Jun 26 '11 at 12:48
  • 1
    And `./incl.sh` resolves to the same path as `cd` + `pwd`. So what is the advantage of changing the directory? – l0b0 May 31 '14 at 12:50
  • Sometimes you need the absolute path of the script, e.g. when you have to change directories back and forth. – András Aszódi Mar 24 '20 at 10:55
  • Thanks for adding this. All the dirname usage alone was making me itch :) I learned this from mentors yrs ago, but with the mildly diff form of DIR="$(cd "$(dirname $0)" && pwd)" – Rondo Oct 29 '21 at 00:40
19

1. Neatest

I explored almost every suggestion and here is the neatest one that worked for me:

script_root=$(dirname $(readlink -f $0))

It works even when the script is symlinked to a $PATH directory.

See it in action here: https://github.com/pendashteh/hcagent/blob/master/bin/hcagent

2. The coolest

# Copyright https://stackoverflow.com/a/13222994/257479
script_root=$(ls -l /proc/$$/fd | grep "255 ->" | sed -e 's/^.\+-> //')

This is actually from another answer on this very page, but I'm adding it to my answer too!

3. The most reliable

Alternatively, in the rare case that those didn't work, here is the bullet proof approach:

# Copyright http://stackoverflow.com/a/7400673/257479
myreadlink() { [ ! -h "$1" ] && echo "$1" || (local link="$(expr "$(command ls -ld -- "$1")" : '.*-> \(.*\)$')"; cd $(dirname $1); myreadlink "$link" | sed "s|^\([^/].*\)\$|$(dirname $1)/\1|"); }
whereis() { echo $1 | sed "s|^\([^/].*/.*\)|$(pwd)/\1|;s|^\([^/]*\)$|$(which -- $1)|;s|^$|$1|"; } 
whereis_realpath() { local SCRIPT_PATH=$(whereis $1); myreadlink ${SCRIPT_PATH} | sed "s|^\([^/].*\)\$|$(dirname ${SCRIPT_PATH})/\1|"; } 

script_root=$(dirname $(whereis_realpath "$0"))

You can see it in action in taskrunner source: https://github.com/pendashteh/taskrunner/blob/master/bin/taskrunner

Hope this help someone out there :)

Also, please leave it as a comment if one did not work for you and mention your operating system and emulator. Thanks!

mhck
  • 873
  • 1
  • 10
  • 20
Alexar
  • 1,858
  • 5
  • 24
  • 34
16

This works even if the script is sourced:

source "$( dirname "${BASH_SOURCE[0]}" )/incl.sh"
Alessandro Pezzato
  • 8,603
  • 5
  • 45
  • 63
  • Could you explain what is the purpose of the "[0]"? – Ray Jul 01 '16 at 13:08
  • 2
    @Ray `BASH_SOURCE` is an array of paths, corresponding to a call-stack. The first element corresponds to the most recent script in the stack, which is the currently executing script. Actually, `$BASH_SOURCE` called as a variable expands to its first element by default, so `[0]` is not necessary here. See [this link](http://mywiki.wooledge.org/BashFAQ/028) for details. – Jonathan H Jun 13 '18 at 09:30
8

You need to specify the location of the other scripts, there is no other way around it. I'd recommend a configurable variable at the top of your script:

#!/bin/bash
installpath=/where/your/scripts/are

. $installpath/incl.sh

echo "The main script"

Alternatively, you can insist that the user maintain an environment variable indicating where your program home is at, like PROG_HOME or somesuch. This can be supplied for the user automatically by creating a script with that information in /etc/profile.d/, which will be sourced every time a user logs in.

Steve Baker
  • 4,323
  • 1
  • 20
  • 15
  • 1
    I appreciate the desire for specificity, but I can't see why the full path should be required unless the include scripts were part of another package. I don't see a security difference loading from a specific relative path (i.e. same dir where the script is executing.) vs a specific fullpath. Why do you say there's no way around it? – Aaron H. Sep 14 '10 at 20:08
  • 4
    Because the directory where your script is executing is not necessarily where the scripts you want to include in your script are located. You want to load the scripts where they are installed at and there is no reliable way to tell where that is at run-time. Not using a fixed location is also a good way to include the wrong (i.e. hacker supplied) script and run it. – Steve Baker Sep 15 '10 at 13:39
  • Using a relative path from a running scripts location is reliable at runtime. And the absolute canonical path to $0 can be reliably found with $(cd "$(dirname $0)" && pwd)" which can also be used to canonicalize the library dir: "$(cd "$(cd "$(dirname $0)" && pwd)/../lib" && pwd)" – Rondo Oct 29 '21 at 01:31
7

I'd suggest that you create a setenv script whose sole purpose is to provide locations for various components across your system.

All other scripts would then source this script so that all locations are common across all scripts using the setenv script.

This is very useful when running cronjobs. You get a minimal environment when running cron, but if you make all cron scripts first include the setenv script then you are able to control and synchronise the environment that you want the cronjobs to execute in.

We used such a technique on our build monkey that was used for continuous integration across a project of about 2,000 kSLOC.

Rob Wells
  • 36,220
  • 13
  • 81
  • 146
4

Shell Script Loader is my solution for this.

It provides a function named include() that can be called many times in many scripts to refer a single script but will only load the script once. The function can accept complete paths or partial paths (script is searched in a search path). A similar function named load() is also provided that will load the scripts unconditionally.

It works for bash, ksh, pd ksh and zsh with optimized scripts for each one of them; and other shells that are generically compatible with the original sh like ash, dash, heirloom sh, etc., through a universal script that automatically optimizes its functions depending on the features the shell can provide.

[Fowarded example]

start.sh

This is an optional starter script. Placing the startup methods here is just a convenience and can be placed in the main script instead. This script is also not needed if the scripts are to be compiled.

#!/bin/sh

# load loader.sh
. loader.sh

# include directories to search path
loader_addpath /usr/lib/sh deps source

# load main script
load main.sh

main.sh

include a.sh
include b.sh

echo '---- main.sh ----'

# remove loader from shellspace since
# we no longer need it
loader_finish

# main procedures go from here

# ...

a.sh

include main.sh
include a.sh
include b.sh

echo '---- a.sh ----'

b.sh

include main.sh
include a.sh
include b.sh

echo '---- b.sh ----'

output:

---- b.sh ----
---- a.sh ----
---- main.sh ----

What's best is scripts based on it may also be compiled to form a single script with the available compiler.

Here's a project that uses it: http://sourceforge.net/p/playshell/code/ci/master/tree/. It can run portably with or without compiling the scripts. Compiling to produce a single script can also happen, and is helpful during installation.

I also created a simpler prototype for any conservative party that may want to have a brief idea of how an implementation script works: https://sourceforge.net/p/loader/code/ci/base/tree/loader-include-prototype.bash. It's small and anyone can just include the code in their main script if they want to if their code is intended to run with Bash 4.0 or newer, and it also doesn't use eval.

konsolebox
  • 72,135
  • 12
  • 99
  • 105
  • 3
    12 kilobytes of Bash script containing over 100 lines of `eval`ed code to load dependencies. *Ouch* – l0b0 May 31 '14 at 12:56
  • @l0b0 If you mean about the scripts provided there, eval'ed code applies only as help for the generic form. Bash and other modern shells don't need it. I assume you read the universal one i.e. `loader.sh`? – konsolebox May 31 '14 at 12:58
  • 1
    Exactly one of the three `eval` blocks near the bottom is always run. So whether it's needed or not, it sure is using `eval`. – l0b0 May 31 '14 at 13:01
  • 4
    That eval call is safe and it's not used if you have Bash 4.0+. I see, you are one of those old-time scripters who think `eval` is pure evil, and doesn't know how to make good use of it instead. – konsolebox May 31 '14 at 13:02
  • Perhaps you could actually suggest a better solution for "flagging" a script for shells not supporting associative arrays? :) – konsolebox May 31 '14 at 13:07
  • 1
    I don't know what you mean by "not used", but **it is run.** And after some years of shell scripting as part of my job, yes, I'm more convinced than ever that `eval` is evil. – l0b0 May 31 '14 at 13:07
  • 1
    Two portable, simple ways to flag files come to mind instantly: Either referring to them by their inode number, or by putting NUL-separated paths into a file. – l0b0 May 31 '14 at 13:09
  • I won't argue with `eval` being evil these days as it's now more like a religion. There are just many ways to prove that eval is not as unsafe as people usually think it is e.g. http://stackoverflow.com/a/18125979/445221. It just depends on the scripter really if he knows what he's doing. – konsolebox May 31 '14 at 13:21
  • I don't think any shell script can portably scan inode numbers? And I hope you don't actually consider a slow IO solution for flagging, or even yet using external tools? Maybe you even intend to use db. – konsolebox May 31 '14 at 13:22
3

Steve's reply is definitely the correct technique but it should be refactored so that your installpath variable is in a separate environment script where all such declarations are made.

Then all scripts source that script and should installpath change, you only need to change it in one location. Makes things more, er, futureproof. God I hate that word! (-:

BTW You should really refer to the variable using ${installpath} when using it in the way shown in your example:

. ${installpath}/incl.sh

If the braces are left out, some shells will try and expand the variable "installpath/incl.sh"!

Rob Wells
  • 36,220
  • 13
  • 81
  • 146
2

This should work reliably:

source_relative() {
 local dir="${BASH_SOURCE%/*}"
 [[ -z "$dir" ]] && dir="$PWD"
 source "$dir/$1"
}

source_relative incl.sh
Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
2

I put all my startup scripts in a .bashrc.d directory. This is a common technique in such places as /etc/profile.d, etc.

while read file; do source "${file}"; done <<HERE
$(find ${HOME}/.bashrc.d -type f)
HERE

The problem with the solution using globbing...

for file in ${HOME}/.bashrc.d/*.sh; do source ${file};done

...is you might have a file list which is "too long". An approach like...

find ${HOME}/.bashrc.d -type f | while read file; do source ${file}; done

...runs but doesn't change the environment as desired.

phreed
  • 1,759
  • 1
  • 15
  • 30
1

Using source or $0 will not give you the real path of your script. You could use the process id of the script to retrieve its real path

ls -l       /proc/$$/fd           | 
grep        "255 ->"            |
sed -e      's/^.\+-> //'

I am using this script and it has always served me well :)

francoisrv
  • 2,457
  • 1
  • 19
  • 13
1

Of course, to each their own, but I think the block below is pretty solid. I believe this involves the "best" way to find a directory, and the "best" way to call another bash script:

scriptdir=`dirname "$BASH_SOURCE"`
source $scriptdir/incl.sh

echo "The main script"

So this may be the "best" way to include other scripts. This is based off another "best" answer that tells a bash script where it is stored

Community
  • 1
  • 1
modulitos
  • 14,737
  • 16
  • 67
  • 110
1

Personally put all libraries in a lib folder and use an import function to load them.

folder structure

enter image description here

script.sh contents

# Imports '.sh' files from 'lib' directory
function import()
{
  local file="./lib/$1.sh"
  local error="\e[31mError: \e[0mCannot find \e[1m$1\e[0m library at: \e[2m$file\e[0m"
  if [ -f "$file" ]; then
     source "$file"
    if [ -z $IMPORTED ]; then
      echo -e $error
      exit 1
    fi
  else
    echo -e $error
    exit 1
  fi
}

Note that this import function should be at the beginning of your script and then you can easily import your libraries like this:

import "utils"
import "requirements"

Add a single line at the top of each library (i.e. utils.sh):

IMPORTED="$BASH_SOURCE"

Now you have access to functions inside utils.sh and requirements.sh from script.sh

TODO: Write a linker to build a single sh file

Xaqron
  • 29,931
  • 42
  • 140
  • 205
0

we just need to find out the folder where our incl.sh and main.sh is stored; just change your main.sh with this:

main.sh

#!/bin/bash

SCRIPT_NAME=$(basename $0)
SCRIPT_DIR="$(echo $0| sed "s/$SCRIPT_NAME//g")"
source $SCRIPT_DIR/incl.sh

echo "The main script"
fastrizwaan
  • 190
  • 1
  • 3
  • Incorrect quoting, unnecessary use of `echo`, and incorrect use of `sed`'s `g` option. -1. – l0b0 May 31 '14 at 12:53
  • Anyway, to get this script work do: `SCRIPT_DIR=$(echo "$0" | sed "s/${SCRIPT_NAME}//")` and then `source "${SCRIPT_DIR}incl.sh"` – Krzysiek Dec 30 '14 at 10:07
0

According man hier suitable place for script includes is /usr/local/lib/

/usr/local/lib

Files associated with locally installed programs.

Personally I prefer /usr/local/lib/bash/includes for includes. There is bash-helper lib for including libs in that way:

#!/bin/bash

. /usr/local/lib/bash/includes/bash-helpers.sh

include api-client || exit 1                   # include shared functions
include mysql-status/query-builder || exit 1   # include script functions

# include script functions with status message
include mysql-status/process-checker; status 'process-checker' $? || exit 1
include mysql-status/nonexists; status 'nonexists' $? || exit 1

bash-helpers includes status output

Alexander Yancharuk
  • 13,817
  • 5
  • 55
  • 55
0

Most of the answers I saw here seem to overcomplicate things. This method has always worked reliably for me:

FULLPATH=$(readlink -f $0)
INCPATH=${FULLPATH%/*}

INCPATH will hold the complete path of the script excluding the script filename, regardless of how the script is called (by $PATH, relative or absolute).

After that, one only needs to do this to include files in the same directory:

. $INCPATH/file_to_include.sh

Reference: TecPorto / Location independent includes

Joaommp
  • 57
  • 8
0

here is a nice function you can use. it builds on what @sacii made. thank you

it will let you list any number of space separated script names to source (relative to the script calling source_files).

optionally you can pass an absolute or relative path as the first argument and it will source from there instead.

you can call it multiple times (see example below) to source scripts from different dirs

#!/usr/bin/env bash

function source_files() {
  local scripts_dir
  scripts_dir="$1"

  if [ -d "$scripts_dir" ]; then
    shift
  else
    scripts_dir="${BASH_SOURCE%/*}"
    if [[ ! -d "$scripts_dir" ]]; then scripts_dir="$PWD"; fi
  fi

  for script_name in "$@"; do
    # shellcheck disable=SC1091 disable=SC1090
    . "$scripts_dir/$script_name.sh"
  done
}

here is an example you can run to show how its used

#!/usr/bin/env bash

function source_files() {
  local scripts_dir
  scripts_dir="$1"

  if [ -d "$scripts_dir" ]; then
    shift
  else
    scripts_dir="${BASH_SOURCE%/*}"
    if [[ ! -d "$scripts_dir" ]]; then scripts_dir="$PWD"; fi
  fi

  for script_name in "$@"; do
    # shellcheck disable=SC1091 disable=SC1090
    . "$scripts_dir/$script_name.sh"
  done
}

## -- EXAMPLE -- ##
# assumes dir structure:
# /
#   source_files.sh
#   sibling.sh
#   scripts/
#     child.sh
#   nested/
#     scripts/
#       grandchild.sh

cd /tmp || exit 1

# sibling.sh
tee sibling.sh <<- EOF > /dev/null 
  #!/usr/bin/env bash
  
  export SIBLING_VAR='sibling var value'
EOF

# scripts/child.sh
mkdir -p scripts
tee scripts/child.sh <<- EOF > /dev/null
  #!/usr/bin/env bash

  export CHILD_VAR='child var value'
EOF

# nested/scripts/grandchild.sh
mkdir -p nested/scripts
tee nested/scripts/grandchild.sh <<- EOF > /dev/null
  #!/usr/bin/env bash

  export GRANDCHILD_VAR='grandchild var value'
EOF

source_files 'sibling'
source_files 'scripts' 'child'
source_files 'nested/scripts' 'grandchild'

echo "$SIBLING_VAR"
echo "$CHILD_VAR"
echo "$GRANDCHILD_VAR"

rm sibling.sh
rm -rf scripts nested

cd - || exit 1

prints:

sibling var value
child var value
grandchild var value
microsoftvamp
  • 108
  • 2
  • 6
-5

You can also use:

PWD=$(pwd)
source "$PWD/inc.sh"
Django
  • 31
  • 1
  • 9
    You assume that you are in the same directory where scripts are located. It won't work if you are somewhere else. – Luc M May 31 '13 at 17:11