The magic behind shebangs!
These are the notes I took to do my video about shebangs, which is available here:

and here:
Como shebangs funcionam - tiktokt video
Unfortunately, all the videos are in portuguese. However, my notes are available bellow and the code is open source and available here: https://github.com/Dpbm/video-shebang.
Enjoy :)
Notes
- The name cames from Sharp-bang (
#!from music sheet notation). Some say that it’s due to its phonetics (hash + bang). - It especifies an interpreter for this script.
- Bash handles the shebang when the underlying system doesn’t do that.
- It’s usual to set it to
#!/bin/bashto ensure that it will use Bash instead of any other shell. - Now days it’s preferable to use
#!/usr/bin/env bashto get bash from$PATHif installed in a different directory. - The script fallback to its parent
SHELLwhen no shebang is defined. - The shebang is used for portability.
- When it’s not provided, the system raises an
ENOEXEC (Exec Format error)error. filecommand uses the shebang, as well, to see which kind of executable is that.- You don’t need a blank after
#!. - Also accepts relative paths.
- When a program runs in a unix-like system, it executes a kernel
exec()(or any command from this family) function. It take a look at the first 16 bits, searching for a magic number that describes an executable. If it’s no signature is found it return anENOEXECerror. - As time went, this magic part of the file also described how to execute it.
- When the run starts running your script, it first tries to delegate it to the kernel, which search for the shebang and tries to execute the file with a different interpreter. If none was found, the bash itself tries to execute.
- Explicit calling a script via a shell command (like
shorbash) the shebang is ignored. - Using
/usr/bin/envdoesn’t provide complete portability, but it’s a good practice. - Arguments can be split differently in different systems, so it may break in some occasions.
- The path of the script is passed via argv to the interpreter.
Calling trace
execve -> do_execveat_common -> create a linux_binprm containing data from it (DEFINE_CLASS -> alloc_bprm ) -> bprm_execve -> exec_binprm -> search_binary_handler -> prepare_binprm -> kernel_read (read the file) ->
it them iterates over a linked list of formats
list_for_each_entry(fmt, &formats, lh) {
the exec.c provides an external function `__register_binfmt` that is used by each type in `binfmt_*.c` to register in the lits, calling like:
core_initcall(init_elf_binfmt);
-> load_binary from struct linux_binprm
if it's a script, it loads `binfmt_script.c` that has the method `load_script` which checks for the shebang
-> open_exec (from exec.c) -> do_open_execat
References
- https://unix.stackexchange.com/questions/149045/why-is-shebang-called-shebang
- https://www.gnu.org/software/bash/manual/bash.html#Shell-Scripts-1
- https://medium.com/@jcroyoaun/a-deeper-view-into-the-shebang-for-linux-scripting-4a26395df49d
- https://stackoverflow.com/questions/3009192/how-does-the-shebang-work
- https://unix.stackexchange.com/questions/268766/what-exactly-happens-when-i-execute-a-file-in-my-shell
- https://crocidb.com/post/kernel-adventures/demystifying-the-shebang/
- https://www.in-ulm.de/~mascheck/various/shebang/
- http://www.faqs.org/faqs/unix-faq/faq/part3/section-16.html
- https://en.wikipedia.org/wiki/Shebang_(Unix)
- https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_script.c#L25
- https://man7.org/linux/man-pages/man2/execve.2.html
- https://man7.org/linux/man-pages/man3/perror.3.html
- https://stackoverflow.com/questions/4689724/where-do-i-find-the-source-code-for-execve
- https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/fs/exec.c#l1376
- https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/binfmts.h
- https://github.com/torvalds/linux/blob/master/fs/namei.c#L4849
- https://docs.kernel.org/admin-guide/binfmt-misc.html
- https://github.com/torvalds/linux/blob/master/include/linux/list.h
- https://github.com/torvalds/linux/blob/master/fs/binfmt_script.c
- https://homepages.cwi.nl/~aeb/std/hashexclam.html
- https://www.oreilly.com/library/view/understanding-the-linux/0596005652/ch20s04.html