I've been learning shell scripting, by reading good tutorials and reading scripts.

I was reading this minecraft init script, and these first lines hit me:

if [ -L $0 ]
    source `readlink -e $0 | sed "s:[^/]*$:config:"`
    source `echo $0 | sed "s:[^/]*$:config:"`

So, these lines launch the script named "config" (where all config values are stored) in the same shell. But why, instead of launching directly the file (with something like "source config", the author of that script reads the name of the original script (whether it is a link or not) and sends the result to sed to replace this name with "config"?

  • @Anthon Please do not name your links “this” or “here”. Give it a meaningful name like “minecraft init script”. – Marco Sep 5 '13 at 9:53

Using source

The command source does not run another script. Merely it pulls the contents of this other script into this script, and then runs its content as if it was originally part of the calling script.

It's basically a mechanism to include other script's contents into the same scope as your own.

Using readlink

This command is there in case the original script was invoked through a link.

Using sed

This script is making use of sed to transform the name of the calling script to the name config.


Say we have this script, named orig.bash:


printf "exectued as:  %s\n" $0
cmd=$(readlink -e $0 | sed "s:[^/]*$:config:")
printf "sourcing as:  %s\n" "$cmd"

This program will do 2 things.

  1. print out the value of $0

    printf "exectued as:  %s\n" $0
  2. print the value of the readlink ... command

    cmd=$(readlink -e $0 | sed "s:[^/]*$:config:")
    printf "sourcing as:  %s\n" "$cmd"

Now let's create a link to this script, link2orig.bash. So we have the following files now in our directory:

# creates link
$ ln -s orig.bash link2orig.bash

# results after
$ ls -l
total 4
lrwxrwxrwx 1 saml saml   9 Sep  5 06:23 link2orig.bash -> orig.bash
-rwxrwxr-x 1 saml saml 126 Sep  5 06:31 orig.bash

Now watch what happens

So now when we run our example script using either it's real name or the link, we're still able to substitute into the caller's argument the string config. Which is another file that has configuration information for our script, that we're sourcing.

$ ./orig.bash 
executed as:  ./orig.bash
sourcing as:  /home/saml/tst/89518/config

$ ./link2orig.bash 
executed as:  ./link2orig.bash
sourcing as:  /home/saml/tst/89518/config

If you'll notice, one of the advantages to this approach is that it's extremely tolerant of being called in different ways and from different locations on the system.

$ ../89518/orig.bash 
executed as:  ../89518/orig.bash
sourcing as:  /home/saml/tst/89518/config

$ ../89518/link2orig.bash 
executed as:  ../89518/link2orig.bash
sourcing as:  /home/saml/tst/89518/config
  • POSIX's . DOES run the other script in the current shell environment. As far as I know, source is just a bashism for . without the safety mechanism of requiring a qualified path, though I could be wrong. As I understand it, the only real difference between the two is source script.sh works whereas . script.sh doesn't unless you . ./script.sh. In any case, the script and any functions or programs it calls, are run. – mikeserv Mar 14 '14 at 1:54
  • The above comment was debated for over an hour in the chatroom. Several examples were presented that demonstrated to all but mikeserv that the answer that I've characterized above is exactly what's going on. – slm Mar 14 '14 at 4:22
  • It's true it was debated. There were some weird semantic arguments on what is run and what is executed and whatever. I think what it came down to was some said a script is not run if it doesn't happen in its own process. In my opinion: echo this is run $(echo and this is run). I thought - and still do - that it was dangerous to suggest a sourced script is not run, because, again, echo 'echo this is run' >~/script.sh ; . ~/script.sh and ( . ~/script.sh ) are both run. In fact, a . run script affects your current environment and so is more risky to run. – mikeserv Mar 14 '14 at 6:06

$0 is the path to the script. It's normally the full path to the script (i.e. an absolute path, starting with /). In the common case where the script is executable and starts with a shebang line, the path will be the full path. If the shell was invoked explicitly, however, $0 is whatever the user typed on the command line, which may not include a path (with cd somedir; bash minecraft, $0 is minecraft). If the shell is invoked with the script on standard input, $0 is the name or path of the shell (with bash <minecraft, $0 is bash).

If the script is accessed via a symbolic link, the script retrieves the full path to that link with all symbolic links expanded via the Linux-specific readlink utility.

The transformation on the name replaces the base name of the script by config. That is, if $0 (or readlink $0 as the case may be) is /path/to/minecraft then the sed command outputs /path/to/config. It works even if the path does not contain any /: minecraft is replaced by config.

The point of this script is to look for a file called config in the same directory as the script, and source that (i.e. read and execute that script in the same context as the calling script). config is a configuration file which is meant to define variables that are used later by the minecraft script.

The script is missing double quotes around variable and command substitutions. It should be written

if [ -L "$0" ]
    source "$(readlink -e "$0" | sed 's:[^/]*$:config:')"
    source "$(echo "$0" | sed 's:[^/]*$:config:')"

This still fails if the path to the script beings with a - or contains a newline but these are rare cases.

The same idea could be written with shell string manipulation constructs (except for the external call to readlink).

case $0 in */*) config=$0;; *) config=./$0;; esac  # make sure there is a / in $config
if [ -L "$config" ]; then config=$(readlink -e -- "$config"); fi

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.