Differencebetween './Example.Sh' and 'Sh Example.Sh'

What is the difference between `./example.sh` and `sh example.sh`

array_name=(value1 ... valuen)

This is how to initializes an array in bash only. When you execute ./example.sh, the shebang line #!/bin/bash tells the system to use bash to execute.

However, when you execute sh example.sh, sh is used to execute. In many Unix systems (like Linux), sh is equivalent to bash. It seems sh is a different shell on your system.

Difference between ./ and sh in UNIX

sh file executes a shell-script file in a new shell process.

. file executes a shell-script file in the current shell process.

./file will execute the file in the current directory. The file can be a binary executable, or it can start with a hashbang line (the first line of the file in form of #!...., for example #!/usr/bin/ruby in a file would signify the script needs to be executed as a Ruby file). The file needs to have the executable flag set.


For example, if you have the script test.sh:

#!/bin/sh

TEST=present

and you execute it with sh test.sh, you'd launch a new sh (or rather bash, most likely, as one is softlinked to the other in modern systems), then define a new variable inside it, then exit. A subsequent echo $TEST prints an empty line - the variable is not set in the outer shell.

If you launch it using . test.sh, you'd execute the script using the current shell. The result of echo $TEST would print present.

If you launch it using ./test.sh, the first line #!/bin/sh would be detected, then it would be exactly as if you wrote /bin/sh ./test.sh, which in this case boils down to the first scenario. But if the hashbang line was, for example, #!/usr/bin/perl -w, the file would have been executed with /usr/bin/perl -w ./test.sh.

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

Whats the difference between running a shell script as ./script.sh and sh script.sh

Running it as ./script.sh will make the kernel read the first line (the shebang), and then invoke bash to interpret the script. Running it as sh script.sh uses whatever shell your system defaults sh to (on Ubuntu this is Dash, which is sh-compatible, but doesn't support some of the extra features of Bash).

You can fix it by invoking it as bash script.sh, or if it's your machine you can change /bin/sh to be bash and not whatever it is currently (usually just by symlinking it - rm /bin/sh && ln -s /bin/bash /bin/sh). Or you can just use ./script.sh instead if that's already working ;)

If your shell is indeed dash and you want to modify the script to be compatible, https://wiki.ubuntu.com/DashAsBinSh has a helpful guide to the differences. In your sample it looks like you'd just have to remove the function keyword.

Why does behavior of `./example` and `sh example` different? (`zsh: text file busy: ./example`)

When you do

$ ./example

you are trying to execute "example" which is being written to. This is not allowed.

When you do

$ sh example

sh is reading "example", then execute what is read. This is fine.



Related Topics



Leave a reply



Submit