Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Purpose of #!/bin/false in bash script

Tags:

bash

shell

While working on a project written in bash by my former colleague, I noticed that all .sh files contain nothing but function definitions start with #!/bin/false, which is, as I understand, a safety mechanism of preventing execution of include-only files.

Example:

my_foo.sh

#!/bin/false
function foo(){
    echo foontastic
}

my_script.sh

#!/bin/bash

./my_foo.sh # does nothing
foo # error, no command named "foo"

. ./my_foo.sh 
foo # prints "foontastic"

However when I don't use #!/bin/false, effects of both proper and improper use are exactly the same:

Example:

my_bar.sh

function bar(){
   echo barvelous
}

my_script.sh

 #!/bin/bash

./my_bar.sh # spawn a subshell, defines bar and exit, effectively doing nothing
bar # error, no command named "bar"

. ./my_bar.sh
bar # prints "barvelous"

Since properly using those scripts by including them with source in both cases works as expected, and executing them in both cases does nothing from the perspective of a parent shell and generate no error message concerning invalid use, what is exactly the purpose of #!/bash/false in those script?

like image 257
Reverent Lapwing Avatar asked Jan 25 '17 10:01

Reverent Lapwing


2 Answers

In general, let’s consider a file testcode with bash code in it

#!/bin/bash
if [ "$0" = "${BASH_SOURCE[0]}" ]; then
  echo "You are executing ${BASH_SOURCE[0]}"
else 
  echo "You are sourcing ${BASH_SOURCE[0]}"
fi

you can do three different things with it:

$ ./testcode
You are executing ./testcode

This works if testcode has the right permissions and the right shebang. With a shebang of #!/bin/false, this outputs nothing and returns a code of 1 (false).

$ bash ./testcode
You are executing ./testcode

This completely disregards the shebang (which can even be missing) and it only requires read permission, not executable permission. This is the way to call bash scripts from a CMD command line in Windows (if you have bash.exe in your PATH...), since there the shebang machanism doesn’t work.

$ . ./testcode
You are sourcing ./testcode

This also completely disregards the shebang, as above, but it is a complete different matter, because sourcing a script means having the current shell execute it, while executing a script means invoking a new shell to execute it. For instance, if you put an exit command in a sourced script, you exit from the current shell, which is rarely what you want. Therefore, sourcing is often used to load function definitions or constants, in a way somewhat resembling the import statement of other programming languages, and various programmers develop different habits to differentiate between scripts meant to be executed and include files to be sourced. I usually don’t use any extension for the former (others use .sh), but I use an extension of .shinc for the latter. Your former colleague used a shebang of #!/bin/false and one can only ask them why they preferred this to a zillion other possibilities. One reason that comes to my mind is that you can use file to tell these files apart:

$ file testcode testcode2
testcode: Bourne-Again shell script, ASCII text executable
testcode2: a /bin/false script, ASCII text executable

Of course, if these include files contain only function definitions, it’s harmless to execute them, so I don’t think your colleague did it to prevent execution.

Another habit of mine, inspired by the Python world, is to place some regression tests at the end of my .shinc files (at least while developing)

... function definitions here ...

[ "$0" != "${BASH_SOURCE[0]}" ] && return 

... regression tests here ...

Since return generates an error in executed scripts but is OK in sourced scripts, a more cryptic way to get the same result is

... function definitions here ...

return 2>/dev/null || :

... regression tests here ...
like image 166
Dario Avatar answered Oct 05 '22 01:10

Dario


The difference in using #!/bin/false or not from the point of view of the parent shell is in the return code.

/bin/false always return a failing return code (in my case 1, but not sure if it is standard).

Try that :

./my_foo.sh //does nothing
echo $? // shows "1", a.k.a failing

./my_bar.sh //does nothing
echo $? // shows "0", a.k.a. everything went right

So, using #!/bin/false not only documents the fact that the script is not intended to be executed, but also produces an error return code when doing so.

like image 35
A.Perrot Avatar answered Oct 04 '22 23:10

A.Perrot