Different ways to execute a shell script

Another way is by calling the interpreter and passing the path to the script to it:

/bin/sh /path/to/script

The dot and source are equivalent. (EDIT: no, they're not: as KeithB points out in a comment on another answer, "." only works in bash related shells, where "source" works in both bash and csh related shells.) It executes the script in-place (as if you copied and pasted the script right there). This means that any functions and non-local variables in the script remain. It also means if the script does a cd into a directory, you'll still be there when its done.

The other ways of running a script will run it in its own subshell. Variables in the script are not still alive when it's done. If the script changed directories, then it doesn't affect the calling environment.

/path/to/script and /bin/sh script are slightly different. Typically, a script has a "shebang" at the beginning that looks like this:

#! /bin/bash

This is the path to the script interpreter. If it specifies a different interpreter than you do when you execute it, then it may behave differently (or may not work at all).

For example, Perl scripts and Ruby scripts begin with (respectively):

#! /bin/perl

and

#! /bin/ruby

If you execute one of those scripts by running /bin/sh script, then they will not work at all.

Ubuntu actually doesn't use the bash shell, but a very similar one called dash. Scripts that require bash may work slightly wrong when called by doing /bin/sh script because you've just called a bash script using the dash interpreter.

Another small difference between calling the script directly and passing the script path to the interpreter is that the script must be marked executable to run it directly, but not to run it by passing the path to the interpreter.

Another minor variation: you can prefix any of these ways to execute a script with eval, so, you can have

eval sh script
eval script
eval . script

and so on. It doesn't actually change anything, but I thought I'd include it for thoroughness.


Most people debug shell scripts by adding the following debuging flags to the script:

set -x     # Print command traces before executing command.
set -v     # Prints shell input lines as they are read.
set -xv    # Or do both

But this means you need to open the file with an editor (assuming you have permissions to edit the file), adding a line like set -x, save the file, then execute the file. Then when you are done you need to follow the same steps and remove the set -x, etc. etc. This can be tedious.

Instead of doing all that, you can set the debugging flags on the commandline:

$ bash -x ~/bin/ducks
+ du -cks -x dir1 dir2 dir3 file1 file2 file3
+ sort -n
+ tail .ducks
123 etc
424 bin
796 total



$ sh -xv ~/bin/ducks  
#!/usr/bin/env bash

# Find the disk hog
# Borrowed from http://oreilly.com/pub/h/15
...
...

Shawn J. Goff made a lot of good points, but did not include the whole story:

Ubuntu actually doesn't use the bash shell, but a very similar one called dash. Scripts that require bash may work slightly wrong when called by doing /bin/sh script because you've just called a bash script using the dash interpreter.

A lot of system scripts (like in init.d, in /etc and so on) have a shebang #!/bin/sh, but /bin/sh is in fact a symbolic link to another shell - in former times /bin/bash, nowadays /bin/dash. But when one of them is invoked as /bin/sh, they behave differently, i.e. they stick to POSIX-compatibility-mode.

How do they do this? Well, they inspect how they were invoked.

Can a shellscript itself test how it was invoked, and do different things, depending on that? Yes, it can. So the way you invoke it can always lead to different results, but of course it is seldom done to annoy you. :)

As a rule of thumb: If you're learning a specific shell like bash, and write commands from a bash tutorial, put #!/bin/bash in the headline, not #!/bin/sh, except where otherwise noted. Else your commands might fail. And if you haven't written a script yourself, invoke it directly (./foo.sh, bar/foo.sh) instead of guessing a shell (sh foo.sh, sh bar/foo.sh). The shebang should invoke the right shell.

And here are two other kinds of invocation:

cat foo.sh | dash
dash < foo.sh