85

What is the difference between sh and source?

source: source filename [arguments]
    Read and execute commands from FILENAME and return.  The pathnames
    in $PATH are used to find the directory containing FILENAME.  If any
    ARGUMENTS are supplied, they become the positional parameters when
    FILENAME is executed.

And for man sh:

NAME
       bash - GNU Bourne-Again SHell

SYNOPSIS
       bash [options] [file]

COPYRIGHT
       Bash is Copyright (C) 1989-2004 by the Free Software Foundation, Inc.

DESCRIPTION
       Bash  is  an sh-compatible command language interpreter that executes commands read from the standard input or from a file.  Bash also incorporates
       useful features from the Korn and C shells (ksh and csh).

       Bash is intended to be a conformant implementation of the IEEE POSIX Shell and Tools specification (IEEE Working Group 1003.2).
1

5 Answers 5

121

When you call source or . (the one is an alias to the other. source cmd not POSIX - kind of bashism), you load and execute a shell script into the current shell process. So you can

  • read variables set in the sourced script,
  • use functions defined within it.
  • and even execute forks and/or subprocess if script do this.

When you call sh, you initiate a fork (sub-process or child) that runs a new session of /bin/sh (which is often a symbolic link to bash). In this case, environment variables set by the sub-script would be dropped when the sub-script terminate.

Caution: sh could be a symlink to another .

Practical sample

For example, if you want to change current working directory by a specific manner, you could not do

$ cat <<eof >myCd2Doc.sh
#!/bin/sh
cd /usr/share/doc
eof

$ chmod +x myCd2Doc.sh

This won't do what you expect:

$ cd /tmp
$ pwd
/tmp
$ ~/myCd2Doc.sh
$ pwd
/tmp

because current working dir is part of environment and myCd2Doc.sh would run in a subshell.

But:

$ source ~/myCd2Doc.sh
$ pwd
/usr/share/doc

Same, for declaring a function:

$ cat >~/myCd2Doc.source <<eof
# Shell source file
myCd2Doc() {
    cd /usr/share/doc
}
eof

$ . ~/myCd2Doc.source
$ cd /tmp
$ pwd
/tmp
$ myCd2Doc
$ pwd
/usr/share/doc

Have a look at mycd function!! (With completion based on Associative Array).

Execution level $SHLVL

$ cd /tmp
printf %b '\43\41/bin/bash\necho This is level \44SHLVL.\n' >qlvl.sh

$ bash qlvl.sh 
This is level 2.

$ source qlvl.sh 
This is level 1.

Recursion (when a script run from itself)

$ cat <<"eoqlvl2" >qlvl2.sh 
#!/bin/bash

export startLevel recursionLimit=5
echo This is level $SHLVL started:${startLevel:=$SHLVL}.
(( SHLVL < recursionLimit )) && ./qlvl2.sh
eoqlvl2
$ chmod +x qlvl2.sh

$ ./qlvl2.sh 
This is level 2 started:2.
This is level 3 started:2.
This is level 4 started:2.
This is level 5 started:2.

$ source qlv2.sh 
This is level 1 started:1.
This is level 2 started:1.
This is level 3 started:1.
This is level 4 started:1.
This is level 5 started:1.

A little futher

$ sed '$a ps --sid $SID fw' qlvl.sh >qlvl3.sh
$ chmod +x qlvl3.sh 
$ export SID
$ read SID < <(ps ho sid $$)
$ echo $SID $$
8983 8983

( Current PID ($$ == process Id) are same identifier than SID (session ID). It's not alway true.)

$ ./qlvl3.sh 
This is level 2.
  PID TTY      STAT   TIME COMMAND
 8983 pts/10   Ss     0:00 /bin/bash
10266 pts/10   S+     0:00  \_ /bin/bash ./qlvl3.sh
10267 pts/10   R+     0:00      \_ ps --sid 8983 fw

$ . qlvl3.sh 
This is level 1.
  PID TTY      STAT   TIME COMMAND
 8983 pts/10   Ss     0:00 /bin/bash
10428 pts/10   R+     0:00  \_ ps --sid 8983 fw

Dot . is an alias of source. So the only difference between two command are slash replaced by space.

And a final test:

$ printf %b '\43\41/bin/bash\necho Ending this.\nsle' \
    'ep 1;exit 0\n' >finalTest.sh

$ bash finalTest.sh 
Ending this.

$ source finalTest.sh
Ending this.

... You may notice a different behaviour between the two syntaxes. ;-)

9
  • 3
    Should also note that any environment variables that aren't exported won't be available to a script that is invoked under a new shell. Commented Mar 14, 2015 at 17:09
  • (The same goes for aliases and shell functions.) Commented Mar 14, 2015 at 17:15
  • @WillVousden: Alias seem not to be exportable at all, you could export variables and/or functions, but not aliases. Commented Mar 14, 2015 at 18:26
  • 1
    @F.Hauri About the relationship between source and ., it's the other way around. source is a bash-specific alias for the standard POSIX syntax .. Please consider modifying your answer to correct the phrasing accordingly.
    – Atralb
    Commented Nov 4, 2020 at 17:13
  • 2
    There was a time when sh was commonly symlinked to bash on Linux systems, but this is no longer true. Even when it is, bash behaves differently when you invoke it through a symlink to sh.
    – tripleee
    Commented Apr 7, 2022 at 7:03
18

The main difference is that they are executed in a different process.

So if you source a file foo which does a cd, the sourcing shell (e.g. your interactive shell in the terminal) is affected (and its current directory will change)

If you execute sh foo the cd does not affect the sourcing shell, only the freshly created sh process running foo

Read the Advanced Bash Scripting Guide.

That difference is not specific to Linux; every Posix implementation would have it.

7
  • 2
    No, do not read the Advanced Bash (in fact they mean Bug) Scripting Guide Commented Dec 9, 2012 at 10:13
  • 1
    Why do you think the ABSG is wrong for newbies? It teaches a lot of useful things.... What alternative guide do you suggest? Commented Dec 9, 2012 at 10:14
  • It teaches the worse bash practices. Just look at the first examples, you'll understand how terrible it is: uppercase variable names, useless uses of cats, ... Commented Dec 9, 2012 at 10:16
  • 1
    Then suggest an alternative better tutorial document. I think it is good enough for newbies... Commented Dec 9, 2012 at 10:18
  • 2
    One alternative is mywiki.wooledge.org/BashGuide and there are links to more at wiki.bash-hackers.org/scripting/tutoriallist
    – tripleee
    Commented Apr 2, 2016 at 7:28
6

As others have mentioned, when you run sh test.sh, any changes that test.sh makes to your shell environment won't persist after the process has ended.

However, also note that any element of your environment that isn't exported (e.g., variables, aliases, and shell functions) won't be available to the code in test.sh when it is executed as a subprocess (i.e. with sh test.sh).

For example:

$ cat > test.sh
echo $foo
$ foo=bar
$ sh test.sh
$ . test.sh
bar

Example 2:

lap@my-ThinkPad:~$ cat test.sh
#!/bin/sh
cd /etc
lap@my-ThinkPad:~$ sh test.sh 
lap@my-ThinkPad:~$ pwd
/home/savoury
lap@my-ThinkPad:~$ source test.sh 
lap@my-ThinkPad:/etc$ pwd
/etc
lap@my-ThinkPad:/etc$ 
3

source (or . ) - runs inside current shell and changes its attribute/environment.

sh do fork and runs in a subshell and hence can't change attributes/environment.

For example

My shell script is -

elite12!rg6655:~/sh_pr [33]$ cat changeDir.sh
#!/bin/bash
cd /home/elt/rg6655/sh_pr/justdir
pwd
echo $$

My Current Shell -

elite12!rg6655:~/sh_pr [32]$ echo $$
3272

Process id of my current shell is 3272

Running with the source -

elite12!rg6655:~/sh_pr [34]$ source changeDir.sh
/home/elt/rg6655/sh_pr/justdir
3272
elite12!rg6655:~/sh_pr/justdir

Observer two things - 1) The process id (3272) is same as my shell, which confirms source executes in the current shell. 2) cd command worked and directory got changed to justdir.

Running with sh -

elite12!rg6655:~/sh_pr [31]$ sh changeDir.sh
/home/elt/rg6655/sh_pr/justdir
13673
elite12!rg6655:~/sh_pr

In this case, process id (13673) is different and directory remains the same which means it is running in a different process or subshell.

1

When you execute a program with sh command:

  • your terminal will use sh or Bourne Shell to execute the program .
  • a new process is created because Bash makes an exact copy of itself . this child process has the same environment as its parent, only the process ID number is different. (this process called forking )
  • you need to need to have execution permission to execute it ( since it is forking )

and when you use source command :

  • you execute the program with your default interpreter
  • you execute the process in your current terminal ( technically your *nix command interpreted )
  • Since the program will be executed in current terminal you dont need to give it execution permission
1
  • You don't need execute permission to invoke by passing it as an argument to sh (e.g. sh test.sh). You only need it if you want to invoke it directly (e.g. ./test.sh). Commented Mar 14, 2015 at 17:11

Not the answer you're looking for? Browse other questions tagged or ask your own question.