Tracing The #!: How The Linux Kernel Handles The Shebang

One of the delights in Bash, zsh, or whichever shell tickles your fancy in your OSS distribution of choice, is the ease of which you can use scripts. These can be shell scripts, or use the Perl, Python or another interpreter, as defined by the shebang (#!) at the beginning of the script. This signature is followed by the path to the interpreter, which can be /bin/sh for maximum compatibility across OSes, but how does this actually work? As [Bruno Croci] found while digging into this question, it is not the shell that interprets the shebang, but the kernel.

It’s easy enough to find out the basic execution sequence using strace after you run an executable shell script with said shebang in place. The first point is in execve, a syscall that gets one straight into the Linux kernel (fs/exec.c). Here the ‘binary program’ is analyzed for its executable format, which for the shell script gets us to binfmt_script.c. Incidentally the binfmt_misc.c source file provides an interesting detour as it concerns magic byte sequences to do something similar as a shebang.

As a bonus [Bruno] also digs into the difference between executing a script with shebang or running it in a shell (e.g. sh script.sh), before wrapping up with a look at where the execute permission on a shebang-ed shell script is checked.

6 thoughts on “Tracing The #!: How The Linux Kernel Handles The Shebang

  1. Fun fact: you can script almost anything.

    just use
    #!/use/bin/env THEAPP

    in the header of the file that you would send as an argument to the app, make the file executable und voila, you can run it.

    I recently started to use those for ansible-playbook.

    1. Another fun fact, posix allows a single argument to be added to the shebang, which becomes the first argument to the interpreter followed by the second argument path to the script.
      (I think “env” allows for more than one arg)

      So if your program normally requires something like “/bin/prog -file in.txt” you can instead start in.txt with “#!/bin/prog -file”

      This is also why, regardless what fancy comment characters you want to support, you should also retain “#” as a comment symbol so the shebang line is ignored.

  2. A major use case for binfmt_misc is running a different processor’s binary on an otherwise incompatible platform.

    e.g. using qemu-user-arm via binfmt_misc to chroot into a Raspberry Pi SD card from an AMD64 machine.

Leave a Reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

This site uses Akismet to reduce spam. Learn how your comment data is processed.