tags:

views:

7333

answers:

9

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?

+7  A: 

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
Chris Boran
This will not work if the script is executed through $PATH. then `which $0` will be useful
Hugo
There is no reliable way to determine the location of a shell script, see http://mywiki.wooledge.org/BashFAQ/028
Philipp
@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.
+3  A: 

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

#!/bin/bash

source $(dirname $0)/incl.sh

echo "The main script"
dsm
+1  A: 

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
+2  A: 

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
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.
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
+6  A: 

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
+1  A: 

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
A: 
SRC=$(cd $(dirname "$0"); pwd)
source "${SRC}/incl.sh"
Max
A: 

This may also be done using Shell Script Loader: http://loader.sourceforge.net/. 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). Scripts based on it may also be compiled to form a single script with the available compiler. A similar function named load() is also provided that will load the scripts unconditionally.

konsolebox
A: 
phreed