Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
November 28, 2020 05:02 pm GMT

What the ! shebang really does and why it's so important in your shell scripts

What exactly happens when we run a file starting with #! (aka shebang), and why some people use #!/usr/bin/env bash.

How the #! works

The #! shebang is used to tell the kernel which interpreter should be used to run the commands present in the file.

When we run a file starting with #!, the kernel opens the file and takes the contents written right after the #! until the end of the line. For didactic purposes, let's consider it saves in a variable called command the string starting after the shebang and ending in the end of line.

After this the kernel tries to run a command with the contents of the command and giving as the first argument the filename of the file we're trying to execute.

Therefore, if you have an executable file called myscript.sh with some shell commands and starting with #!/bin/bash, when you run it, the kernel will execute /bin/bash myscript.sh.

In the examples below you're going to see it very clearly.

Starting with the classic hello.sh:

#!/bin/bashecho "Hello World!"
Enter fullscreen mode Exit fullscreen mode

Assuming this file has the executable permission, when you type this in the command line:

$ ./hello.sh
Enter fullscreen mode Exit fullscreen mode

The kernel will notice the #! in the very first line and then will get what's after it, in this case /bin/bash. And then what is executed has the very same effect of this:

$ /bin/bash hello.sh
Enter fullscreen mode Exit fullscreen mode

Let's use another example using #!/bin/cat. The name of the file is shebangcat:

#!/bin/catAll the contents of this file will beprinted in the screen when it's executed(including the '#!/bin/cat' in the first line).
Enter fullscreen mode Exit fullscreen mode

Let's remember:

  • What's after the shebang: /bin/cat
  • Name of the file: ./shebangcat

Therefore this is what's executed: /bin/cat ./shebangcat

See it by yourself:

$ ./shebangcat#!/bin/catAll the contents of this file will beprinted in the screen when it's executed(including the '#!/bin/cat' in the first line).
Enter fullscreen mode Exit fullscreen mode

Let's take another example to make it very clear that things are like I'm saying. The following file is called shebangecho:

#!/usr/bin/echoThe contents of this file will *NOT* beprinted when it's executed.
Enter fullscreen mode Exit fullscreen mode

Let's check:

$ ./shebangecho./shebangecho
Enter fullscreen mode Exit fullscreen mode

The output was the name of the file because this is what was executed by the kernel /usr/bin/echo ./shebangecho.

Another interesting thing, is that if we pass arguments when calling our script, such arguments will also be passed to the command executed by the kernel. As we can see in the following example called shebangls.sh:

#!/bin/lsThe contents here doesn't matter.
Enter fullscreen mode Exit fullscreen mode

Now, when we run it:

$ ./shebangls.sh./shebangls.sh$ ./shebangls.sh -l-rwxr-xr-x 1 meleu meleu 41 Nov 28 14:42 ./shebangls.sh$ ./shebangls.sh notfound/bin/ls: cannot access 'notfound': No such file or directory./shebangls.sh
Enter fullscreen mode Exit fullscreen mode

Why some people use #!/usr/bin/env?

You probably saw some scripts starting with #!/usr/bin/env bash where you're used to see just #!/bin/bash. The reason of this is to increase the portability of the script (even thought it's a debatable matter, as we're going to see below).

The env command, if used with no arguments, prints a (big) list with all the environment's variables. But if env is used followed by a command, it runs that command in another instance of the shell.

- OK, but how does that influence portability?!

When you use #!/bin/bash you're clearly saying that bash is in the /bin/ directory. This seems to be the default in all Linux distributions, but there are other Unix flavors where it can possibly not happen (for example the bash can be placed in the /usr/bin/). In systems like that your script starting with #!/bin/bash would cause a bad interpreter: No such file or directory.

When you run env bash, the env will search for bash in your $PATH variable, and then run the first one it finds. Usually bash is in /bin/, but a user running your script on some other system can have it in /usr/bin/ or even testing an alternative version in /home/user/bin/bash.

So, in order to make the script have a greater reach and be used in environments other than Linux, some people recommend the use of the env technique.

- But wait! What guarantees that the env will always be in the /usr/bin/?

There are no guarantees...

The recomendation is based in what is commonly seen in the Unix systems. I see /usr/bin/env being used in some modern projects (like RetroPie), but where it's specially useful is when you need to run a python or even a NodeJS script.

Let's take this NodeJS usage as an example. I want to call a NodeJS script just by calling the script's filename. Then I could do something like this:

#!/usr/bin/nodeconsole.log('Hello World from NodeJS');
Enter fullscreen mode Exit fullscreen mode

The problem is that I usually install node via Node Version Manager, instead of using the the distribution's package manager. So, my node is like this:

$ which node/home/meleu/.nvm/versions/node/v14.15.1/bin/node
Enter fullscreen mode Exit fullscreen mode

By any means I want to put #!/home/meleu/.nvm/versions/node/v14.15.1/bin/node in my script!

So, the solution here is to use #!/usr/bin/env node.

And if I don't want to use #! at all?

I strongly recommend you to never write neither run a shell script without a #! shebang!

As we said, the shebang tells to the kernel which interpreter is to be used to run the commands present in the file. If you run a script without specifying the interpreter, the shell will spawn another instance of itself and try to run the commands in the script. Which means that it will execute whatever commands found in the file, even if it was written for zsh, ksh, dash, fish, node, python, or whatever.

Summing up: Always start your scripts with a #! shebang. Preferably with #!/usr/bin/env.

Links


Original Link: https://dev.to/meleu/what-the-shebang-really-does-and-why-it-s-so-important-in-your-shell-scripts-2755

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To