Shell: Magic #!

Bruce Wen
3 min readOct 6, 2021

#! is always the first line of most shell scripts. How magic is it? Let’s explore.

Photo by Muhammad Haikal Sjukri on Unsplash

How to pronounce it?

#! has several different names. The most popular one is shebang. Other names include hashbang, poundbang, and hash-pling.

Who invented “#!”?

The shebang was introduced by Dennis Ritchie between Edition 7 and 8 (UNIX)at Bell Laboratories.

How does #! work?

If a file is executable and it has #!as start of the first line, then program loader will parse the remaining of the first line as interpreter. Then when the file is executed, the program loader will run the interpreter and pass the file path to the interpreter as first argument —that’s why $0 points to the shell script itself.

Let’s assume we created one file /home/wenijinew/test.sh with below content and make it executable( chmod ):

#!/usr/bin/bash 
echo "Hello World!"

Then, we execute it:

> /home/wenijinew/test.sh
Hello World!

It actually works in the way with below command:

/usr/bin/bash /home/wenijinew/test.sh

It works well even we removed the #!line in test.sh file as it already explicitly use bash to interpret the script file.

More magic #!

In fact, the script test.sh above can run directly without #!:

> /home/wenijinew/test.sh
Hello World!

Why?!

In fact, this is supported by kernel. The script will be interpreted by user’s default shell — normally, bash.

We can witness it by add one more line in test.sh :

ls -l /proc/$$/exe

Then, run it again:

> /home/wenijinew/test.sh
Hello World!
lrwxrwxrwx 1 wenijinew users 0 Oct 6 05:26 /proc/16261/exe -> /usr/bin/bash

Now, we can see the process is actually /usr/bin/bash !

Therefore, if you put non-bash commands in the script file, it would not work:

print("Hello, World!")

It will has below error as above code cannot be interpreted by bash:

> /home/wenijinew/test.sh
/home/wenijinew/test.sh: line 1: syntax error near unexpected token `'Hello, World''
/home/wenijinew/test.sh: line 1: `print('Hello, World')'

However, if you put below shebang line, it will work (even it’s .sh file):

#!/usr/bin/python

Since it actually run below command as aforementioned:

/usr/bin/python /home/wenijinew/test.sh

What can put after #!

No limitation! That’s true but maybe not what you want. For example, you can put ls after #!:

#!/usr/bin/ls 
echo "Hello World!"

Then, when you run test.sh , it will just print path of it:

> /home/wenijinew/test.sh
/home/wenijinew/test.sh

Because that’s what can ls do for you. It cannot interpret anything in the script file actually.

Thus, program loader will just parse the part after #!and try to run it by passing the shell script file path as first argument. It’s not required that the path after #!must be shell interpreter.

There is one special case which is useful: put /usr/bin/false after #! to avoid standalone execution of the script directly. This can be used for those scripts to run after login or logout. For example, .login , .logout , .bashrc and so on. But you can also source it.

Thanks for reading and happy coding!

--

--