If you want the geekish explanation, Wikipedia has a good overview of shebang. Here, I want to look at it from a more pragmatic view.
You've undoubtedly seen shell scripts begin with "#!/bin/bash" and you may have noticed Perl scripts that begin with "#!/usr/bin/perl". You may have also noticed that you seemingly don't really need "#!/bin/bash" but "#!/usr/bin/perl" isn't quite so optional.
Let's take a look at a simple and pointless shell script. We'll run it with and without "#!/bin/bash" and see what happens.
#!/bin/bash while : do sleep 30 done
I'll call that "t.sh" and run it as "./t.sh":
$ ./t.sh &  836
A "ps" shows what's running:
464 ttys001 0:00.02 -bash 836 ttys001 0:00.00 /bin/bash ./t.sh 837 ttys001 0:00.00 sleep 300
Now we'll make a copy of that ("tcopy.sh") but take out the first line:
while : do sleep 30 done
We'll run that:
$ ./tcopy.sh &  848 $ ps PID TTY TIME CMD 464 ttys001 0:00.02 -bash 836 ttys001 0:00.00 /bin/bash ./t.sh 837 ttys001 0:00.00 sleep 30 848 ttys001 0:00.00 -bash 849 ttys001 0:00.00 sleep 30
One difference stands out: the version with "#!/bin/bash" shows us "t.sh" in the "ps" listing, but the other version just shows "bash" (actually, "-bash", but that's a story for another day).
Let's kill those off:
$ kill 836 848 $ ps PID TTY TIME CMD 464 ttys001 0:00.03 -bash 855 ttys001 0:00.00 sleep 30 992 ttys001 0:00.00 sleep 30 - Terminated: 15 ./t.sh + Terminated: 15 ./tcopy.sh
Notice that the "sleep"'s are still running - that too is a story for another day, but they'll go away soon enough. So, right away we see one advantage of the shebang: it lets us see the name of a shell script in "ps".
We're not quite done. Let's create a Perl script:
What happens when we use that?
$ ./t.pl &  1050 $ ps PID TTY TIME CMD 464 ttys001 0:00.04 -bash 1050 ttys001 0:00.01 /bin/bash ./t.sh 1051 ttys001 0:00.00 sleep 30
Same thing, right? How about we exec "tcopy.sh" from that script instead?
$ cat t.pl #!/usr/bin/perl exec("./tcopy.sh"); $ ./t.pl &  1085 $ ps PID TTY TIME CMD 464 ttys001 0:00.05 -bash 1085 ttys001 0:00.01 sh ./tcopy.sh 1086 ttys001 0:00.00 sleep 30
1050 ttys001 0:00.01 /bin/bash ./t.sh 1085 ttys001 0:00.01 sh ./tcopy.sh
So that's another thing: if our shebang-less script is run by something other than bash, it's /bin/sh that ends up running it. Certainly in this case it doesn't matter - sh or bash will do the same thing. But other scripts might have bash specific commands and /bin/sh would fail or behave badly.
So is there any reason to leave out the shebang?
Let's make t.sh and tcopy.sh very simple:
$ cat t.sh; echo " ";cat tcopy.sh #!/bin/bash x=1024 echo $x exit x=1024 echo $x exit
Now let's have them race each other:
$ time ./t.sh;time ./tcopy.sh 1024 real 0m0.003s user 0m0.001s sys 0m0.001s 1024 real 0m0.001s user 0m0.001s sys 0m0.001s
Whoah! What's that?? Why is the shebangless version faster?
The man page for bash tells us the difference in the "COMMAND EXECUTION" section:
So, it's quicker to fail and run the subshell than to succeed. Not by much, of course, but the difference is real. I can't imagine a circumstance where it would actually matter unless you had some strange system that was constantly firing of short little shell scripts to do this and that - you'd need to be doing an awful lot of that to even notice~
Got something to add? Send me email.
More Articles by Anthony Lawrence © 2013-07-28 Anthony Lawrence