I think you’re misunderstanding some things.
An “interpreter” is a specific program that interprets (i.e., reads and parses a file, and statement-by-statement executes some precompiled machine code that is part of the interpreter’s binary - different from natively compiled executables as they directly contain machine code, and don’t need a separate binary to contain machine code) a specific language.
There are lots of different interpreters, and they all implement different languages. The ones you mention here are:
/bin/sh
is generally a POSIX shell implementation, which can interpret a very specific version of the general family of shell scripting languages, as specified by the POSIX standard./bin/bash
is usually an interpreter for a non-POSIX-compliant shell scripting language./usr/bin/python
is usually an interpreter for the python language, which isn’t a shell scripting language at all and won’t know what to do with a shell script whatsoever.
Generally, interpreters are completely incompatible - one interpreter won’t understand a script written for another interpreter at all. Other interpreters include
- nodejs
- php
- ruby
- perl
- awk
The reason this may be confusing is that the shell scripting family of languages (POSIX sh, bash, zsh, ksh, …) are somewhat compatible to different degrees - however, bash has certain extensions on top of POSIX sh that won’t work in any of the others, zsh has extensions that won’t work in any of the others, and they all have subtle incompatibilities with POSIX sh. fish, powershell and nushell on the other hand are completely incompatible with POSIX.
Even between different versions of the same interpreter there may be incompatibilities.
That means these are not equivalent at all:
Even if all the interpreters were equivalent to one another, that won’t work; the way the kernel runs an executable file is to check the first two characters, and if they are #!
it will interpret everything until the next newline as the “interpreter”, which it will execute and pass the rest of the file. Your script will therefore always be executed by /bin/bash
, which will ignore all the subsequent lines since they are comments to bash.
There’s simply no concept of a “next available interpreter”. You need to get the interpreter right first try.
@Solene 's pragmatic solution is:
#!/usr/bin/env bash
#*Check if running in NixOS environment
if [ -f /run/current-system/sw/bin/sh ]; then
echo -e "\033[1;34mRunning in NixOS environment\033[0m"
else
echo -e "\e[31m ERR\e[0m: Non NixOs system"
fi
echo -e "\033[1;34m Hello Worldt\033[0m"
env
is not an interpreter. Instead, it’s a little program that manipulates the environment before it executes another program. If you just give it a single argument, it will look said argument up in $PATH
, and execute the first binary with that name it finds, and in this case pass on the rest of the file to that binary.
I.e., that first line means “check where the bash
binary is on this system, and run it to interpret this script”.
This is the most portable way of executing bash - it should work on all modern-ish systems, as this behavior of /usr/bin/env
has been a standard for a while and is part of the POSIX spec.
Ironically, it’s also completely impure, so I understand why people are concerned about the whole impurity of bash being in /bin
thing less and less.
This is kind of true, actually. Since sh
generally refers to the POSIX implementation of the shell language, it tends to be executable by most shell scripting interpreters. Furthermore, any Unix-derivative worth its salt tends to implement POSIX, and therefore has an sh
somewhere. It is not guaranteed to be in /bin/sh
, as this is not part of the spec, but it generally will be.
So using /bin/sh
instead of /bin/bash
is a pretty safe bet for compatibility, assuming you don’t actually need bash extensions.
That said, if you want to do it 100% correctly you should still modify the shebang on every system you deploy it to (by manually opening the file, and editing the sh
location to whatever command -v sh
gives you). NixOS has some utility functions available for doing this automatically.
If all of this seems inane, please don’t blame NixOS. It’s the POSIX developer’s fault, but it’s tough going up against standards older than most a lot of (apparently only 35, who’d-a thunk) people on this forum.
35 years ago opening every script you install and editing its first line (or running sed
on it) was probably more acceptable.