Difference Between Source and ./ Execution of Linux Scripts

What is the difference between using `sh` and `source`?

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 shell.

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:

$ 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 bash 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. ;-)

What's the difference between: . [script] or source [script], bash [script] or $SHELL [script], and ./ [script] or [script]?

. script and source script execute the contents of script in the current environment, i.e. without creating a subshell. On the upside this allows script to affect the current environment, for example changing environment variables or changing the current work directory. On the downside this allows script to affect the current environment, which is a potential security hazard.

bash script passes script to the bash interpreter to execute. Whatever shebang is given by script itself is ignored. ("Shebang" referring to the first line of script, which could e.g. read #!/bin/bash, or #!/usr/bin/perl, or #!/usr/bin/awk, to specify the interpreter to be used.)

$SHELL script passes script to whatever is your current shell interpreter to execute. That may, or may not, be bash. (The environment variable SHELL holds the name of your current shell interpreter. $SHELL, if running bash, is evaluated to /bin/bash, with the effect detailed in the previous paragraph.)

./script executes the contents of a file script in the current work directory. If there is no such file, an error is generated. The contents of $PATH have no effect on what happens.

script looks for a file script in the directories listed in $PATH, which may or may not include the current work directory. The first script found in this list of directories is executed, which may or may not be the one in your current work directory.

Difference between source and ./ execution of linux scripts

check if variable "a" is set in your current shell:

set | grep '^a='

Remember that once you source script to your current shell,
all it's global variables are there until unset or you exit the current shell.
You may want to start a new shell, source the script, end exit shell to perform valid tests.

I don't know the context of your problem, but you may want to export some key variables to have their copies in every subprocess.

sourced vs executed scripts: in which shell process do they run?

Do a little experiment;

$ echo $$ # print PID of current shell
1234
$ echo 'echo $$' > test.sh # make a little shell script that just prints its PID
$ ls -l test.sh
-rw-rw-r-- 1 hlub hlub 10 Sep 26 20:01 test.sh
# no x'es: not executable
$ source test.sh # source it....
1234
# OK, that worked, even without execute permission,
# .... and we get the same PID:
# .... apparently "source" runs commands in the current shell
$ ./test.sh
zsh: permission denied: ./test.sh
# Oops! need execute permission
$ chmod a+x test.sh # make the script executable
$ ls -l test.sh
-rwxrwxr-x 1 hlub hlub 10 Sep 26 20:02 test.sh
# x'es, just as I expected!
$ ./test.sh
5678
# OK, so now the script executes in a new process!

Using dot or source while calling another script - what is the difference?

There is no difference.

From the manual:

source

source filename

A synonym for . (see Bourne Shell Builtins).

difference between #./ and #. ./

./foo executed foo if it's marked as executable and has a proper shebang line (or is an ELF binary). It will be executed in a new process.

. ./foo or . foo loads the script in the current shell. It is equal to source foo

With your example code you need to use the second way if you want the exported variables to be available in your shell.

Difference between different ways of running shell script

sh test.sh

Tells the command to use sh to execute test.sh.

./test.sh

Tells the command to execute the script. The interpreter needs to be defined in the first line with something like #!/bin/sh or #!/bin/bash. Note (thanks keltar) that in this case the file test.sh needs to have execution rights for the user performing this command. Otherwise it will not be executed.

In both cases, all variables used will expire after the script is executed.

. ./test.sh

Sources the code. That is, it executes it and whatever executed, variables defined, etc, will persist in the session.

For further information, you can check What is the difference between executing a bash script and sourcing a bash script? very good answer:

The differences are:

  • When you execute the script you are opening a new shell, type
    the commands in the new shell, copy the output back to your current
    shell, then close the new shell. Any changes to environment will take
    effect only in the new shell and will be lost once the new shell is
    closed.

  • When you source the script you are typing the commands in your
    current shell. Any changes to the environment will take effect and stay in your current shell.



Related Topics



Leave a reply



Submit